Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

useLayoutEffectとは?何ができるの?

対象読者: 「useEffectより早く発火するやつでしょ?」くらいのふんわりとした理解をしている方

useLayoutEffect とは何か?

まずズバリ結論から言うと、「ブラウザが要素をレイアウトしたあと、画面に描画する前に同期的にJSを実行するためのフック」です。

さっぱりわからないと思うので順を追って説明します。

ブラウザのレンダリングの流れ

useLayoutEffect の動作を知るには、ブラウザのレンダリング処理について知らなければなりません。

ブラウザのレンダリングはおおむね次のような流れです。

  1. DOM が変更される
  2. レンダリングツリーを構築
  3. レンダリングツリーをもとにそれぞれの要素の位置と大きさを計算(Layout)
  4. 描画内容を計算(Paint)
  5. Paint した内容を合成(Composite)
  6. ユーザーの画面に実際に反映

このように、ユーザーに見えるようになるまでに複数の工程があります。

useLayoutEffect はいつ実行されるか?

useLayoutEffect は React がコンポーネントをレンダリングして、それを元にDOM を変更して、ブラウザが Layout したあとに実行されます。つまり要素の大きさと位置の情報は取れますがユーザーには見えていない段階です。

(引用: hook-flow)

useLayoutEffect がチラつき抑制になるのはなぜ?

よく useEffect だとチラつくから useLayoutEffect を使うという説明があります。ここまでの説明でなぜチラつきを抑えられるかも説明ができます。

もし、useLayoutEffect をした結果 DOM が変更になったらどうなるでしょうか?答えは Paint が行われず、Layout から再実行される です。ユーザーには useLayoutEffect が実行されたあとの画面しか見えません。そのため変更前の画面はユーザーに見えません。

useEffect は画面に反映された後に実行され、再度 Layout → Paint が行われるためチラつくのです。

useLayoutEffect が同期的とはどういうことか?

Paint は Layout のあと、JS の実行が止まり次第走ります。つまり同期的処理は終わるまで Paint をブロッキングします。逆に言えば非同期的処理は Paint をブロッキングしません。そのため同期処的理でなければなりません。

参考

https://leap-in.com/ja/lets-learn-how-to-browser-works/ https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#render-behavior-edge-cases https://ja.reactjs.org/docs/hooks-reference.html#uselayouteffect