✍️

useEffectとuseLayoutEffectの違いについてまとめる

2022/07/03に公開

最近見たコードでuseEffectuseLayoutEffectがごちゃまぜになっているソースコードを見かけた。
リファクタリングする際に、大まかにわかっているつもりだったが説明がうまくできないと感じたのでまとめてみる。
初心者のため間違っている部分も見受けられると思います。
間違っている箇所があればご指摘いただけますと大変助かります。

useEffect

useEffectとは

公式ドキュメントのフックAPIリファレンスによると

副作用を有する可能性のある命令型のコードを受け付けます。

とのこと。
ちなみにここでいう副作用とは

ある機能がプログラム上のデータを変化させ、それ以降の演算の結果に影響を与えることを指します。

ということです。
関数コンポーネント本体以外のデータなどを変化させる処理などをイメージすればよいのでしょうか。
ドキュメンには例として

  • DOM の書き換え
  • データの購読
  • タイマー
  • ロギング
  • あるいはその他の副作用

と記載してあるので概ね間違ってはいなさそうです。

副作用とは React の純粋に関数的な世界から命令型の世界への避難ハッチであると考えてください。

とも書いてあるので、コンポーネントという純粋関数から関係のないロギングなどの処理を切り離すためのものという感じですかね。
他にもコンポーネントが消える際のリソースのクリーンアップにも使用されるようです。

新しい副作用を実行する前に前回の副作用はクリーンアップされます。

とのことなので下記の用にレンダリング毎にリソースを作り直す必要がある場合などにも使用します。

// 公式ドキュメントから抜粋
useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

実行タイミング

useEffectに渡した処理はどのタイミングで実行されるのかというと

useEffect に渡された関数はレイアウトと描画の後で遅延されたイベントとして実行されます。

と記載のある通り、レンダリング後に実行されます。
React18以降は

  • クリックなどのユーザー入力の結果として描画が起こる場合
  • flushSyncでラップされた更新の結果で描画が起こる場合

にはレンダリング前に同期的に呼び出されるようです。
ただし更新が遅延すること自体は変らないようなので基本的にはレンダリング後に実行されると考えても差し支えなさそうです。

useLayoutEffect

useLayoutEffectとは

シグネチャはuseEffectと全く一緒です。
異なるのはDOMの変更があったたときに同期的に副作用が呼び出されるという部分です。
スケジュールされた更新はブラウザに描画される前に同期的に実行されます。

可能な場合は画面の更新がブロックするのを避けるため、標準の useEffect を優先して使ってください。

とのことなので
基本的にはuseEffectを優先するべきで、その処理を行わないと正しくレンダリングできない場合などに使うべきフックのようです。

まとめ

基本的に副作用のある処理を実行するときはまずuseEffectを使うべきのようです。
useLayoutEffectの使いどころとしては以下の記事のようにレンダリング前に実行しないと見た目に影響が出る処理が存在する場合ですかね。
useEffectのちらつきを無くしたいときの対処法【useLayoutEffect】
ただし、計算量のある処理などの場合は、同期的にした際にレンダリングがブロックされる時間が長くなります。
そのためユーザー体験が悪くなるのでなるべく使うべきでないというのが注意点でしょうか。

GitHubで編集を提案

Discussion