useEffect/useLayoutEffect周りで絶賛ハマり中で、また React17におけるuseEffectの破壊的変更を理解する の記事が面白かったので、自分の手元で動かしてみて理解を深めてみた。調査したことを自分用のメモとして取っておく。React 17でuseEffect周りで破壊的な変更が入るということなので、Reactのバージョンは17.0.0-rc.1で試した。
気になっていたこと
- 親子のコンポーネントでuseEffectがどの順で実行されているのか
- useLayoutEffectが途中にあるとどのような挙動をするか
- useEffect/useLayoutEffectとレンダリングやDOMへの反映の実行順序
調査メモ
shibayu36/typescript-playground/react-playground あたりで試した。react-playground ディレクトリ以下でyarn installしてyarn startしたら試せる。
以下2つのエンドポイントを実装し、行き来しながらどのようなログが出るか見た。
/
: src/App.tsx/effect-order
: src/EffectOrder.tsx
/
-> /effect-order
へアクセスした時は以下のようなログが出る。この時、LayoutEffect useLayoutEffect
では同期的に2秒待ちを入れていたので、実際に画面に表示されるのは2秒後となった。Child useEffectは2秒待ちを入れていたが、画面表示には影響せず、その代わり後続のuseEffectは遅延された。
EffectOrder rendering Effect rendering LayoutEffect rendering Child rendering Home ref null Child ref <div>Child Component</div> LayoutEffect ref <div>…</div> LayoutEffect useLayoutEffect Effect ref <div>…</div> EffectOrder ref <div>…</div> Child useEffect Effect useEffect EffectOrder useEffect
/effect-order
-> /
のときは以下のようなログが出る。こちらもuseLayoutEffectのcleanupに2秒待ちを入れていたのでそれが実行されるまでDOMへの反映も遅延された。
Home rendering EffectOrder ref null Effect ref null LayoutEffect useLayoutEffect cleanup LayoutEffect ref null Child ref null Home ref <header class="App-header">…</header> EffectOrder useEffect cleanup Effect useEffect cleanup Child useEffect cleanup
この実行から分かったことは
- Virtual DOMの構築はたぶん「... rendering」というログのところで実行されていそうなので、useEffect/useLayoutEffect関係なく作られてそう
- 実際のDOM構築はたぶん「... ref」というログのところで実行されていそうなので、useLayoutEffectはこのタイミングで実行される。実際のDOM構築が全て終わらないと画面表示出来ないので、useLayoutEffectで重い処理をするとユーザーへの描画タイミングが遅れる
- useEffectはDOM構築が終了した後に実行されるので表示には影響しない。マウント時は子から親の順で実行され、アンマウント時は親から子の順で実行される。途中で重い処理が行われていると後続のuseEffectは遅延する。
フェーズとしては、Virtual DOM構築フェーズ、実際のDOM構築 & useLayoutEffect実行フェーズ、useEffect実行フェーズがある感じか?
まとめ
ReactのuseEffect/useLayoutEffectやレンダリングの実行順について調べたので自分用のメモを残してみた。Reactのドキュメントに書いてあるような内容を再確認しただけにはなったが、自分の手を動かしたためか、もっと実感を持って理解できたような気がする。