useEffectとuseLayoutEffectの違いについてまとめる
最近見たコードでuseEffect
とuseLayoutEffect
がごちゃまぜになっているソースコードを見かけた。
リファクタリングする際に、大まかにわかっているつもりだったが説明がうまくできないと感じたのでまとめてみる。
初心者のため間違っている部分も見受けられると思います。
間違っている箇所があればご指摘いただけますと大変助かります。
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】
ただし、計算量のある処理などの場合は、同期的にした際にレンダリングがブロックされる時間が長くなります。
そのためユーザー体験が悪くなるのでなるべく使うべきでないというのが注意点でしょうか。
Discussion