ブログblog

useEffectを使用するべき場所なのか

Writer: tokuyasu 更新日:2024/06/07

こんにちは、デジナーレ福岡オフィスの徳安です。
今回は、useEffectを使用すべきではない場所で、
useEffectを使用することを減らすため、
公式ドキュメントを読んで、学んだ内容をまとめようと思います。

1. props または state に基づいて state を更新する

*firstNameとlastNameの2つのstate変数を結合して、
「fullName」という新たな値を作成する場合

Bad

結合する2つの変数の値が更新されたタイミングで、fullNameの値も更新したい。
useEffectの第二引数に、firstNameとlastNameを追加し、更新する。

既存の props や state から作成したいものに関しては、
Reactのレンダリング中に計算されるため、stateでの管理は行わない

Good レンダリング中に計算する

上記のように最適化することにより、高速で、シンプルかつ、エラー、バグの原因を減らすことができる。

2. 重たい計算のキャッシュ

* 親コンポーネントから受け取ったpropsを、getFilteredTodosの引数として使用する場合

Bad 冗長なuseState と 不必要なuseEffect

先ほどの例と同様で、propsの値が変更された場合は、
Reactが自動的に差分を計算して、
必要な際にレンダリングを行ってくれるので、useEffectは必要ない

Good 関数内の処理が、重たい計算でなければこれでOK

*関数内の処理が重い場合**
関数の処理とは関係のないstateが更新されたタイミングで、
余計なキャッシュを使用している場合は、useMemo()を使用して、関数をメモ化する。

3. props が変更されたときにすべての state をリセットする

* propsで受け取った値が変更されているのに、commentの値がリセットされていない場合

Bad propsの値が変更になった際に、commentの値が空になるようにする

このように記述すると、ProfilePageとProfilePageが持つ子コンポーネントは、
まず古い値でレンダリングされ、その後に再度レンダリングされることになる為、非効率。

Good コンポーネントを 2つに分割し、親コンポーネントから子コンポーネントに key 属性を渡す

4. アプリケーションの初期化

アプリが読み込まれるときに、一度だけ実行したいロジックがある場合

Bad  一度しか実行したくないロジックをトップレベルのコンポーネントのuseEffect内に書く

上記は開発環境では2度実行されてしまう。
このように記述すると、
関数が 2 回呼び出されることを想定していないため
認証トークンを無効化させてしまう可能性などがある。

Good トップレベルに変数を使用することで再実行をスキップする

 

Good モジュールの初期化時やアプリのレンダリング前に実行する

5. 外部ストアへのサブスクライブ

* サードパーティーライブラリやブラウザに組み込まれたAPIなどを使う場合

Bad  useEffect内で手動でサブスクライブする

上記のような処理はuseEffect内で行われるのが一般的だが、
外部ストアへサブスクライブする際には、
useSyncExternalStoreを使用することが推奨されている。

Good

6. データのfetch

* イベントによってデータのfetchを行う必要がある場合

Bad  クリーンアップなしでfetch

queryとpageの値が変更になったときに、
ネットワークからのデータと同期させる必要がある。
エフェクトに記述するべきだが、このコンポーネントでは、バグが生じる可能性がある。

例えば検索などのinputエリアがある場合、
ユーザーが入力フィールドにhelloと打つと、
h, he, hel, hell, helloのそれぞれでfetchされることになる。

しかしこの5つのレスポンスの結果がfetchした順序で返ってくる保証がない
この現象のことを、race condition(競合状態)と呼ぶ。

これを解消するためには、クリーンアップを追加する必要がある。

Good

クリーンアップの追加により、最後にリクエストしたもの以外のレスポンスが無視される。

コンポーネント内の生の useEffect の呼び出しが少なければ少ないほど、
アプリケーションのメンテナンスは容易になるため、
データ取得ロジックをカスタムフックに移動することも検討する必要がある。

まとめ

ReactのuseEffectは、副作用の管理に便利な機能ですが、
考えなしに使用すると、思わぬ場所でバグが発生したりエラーの原因となってしまうことがあります。

私自身もuseEffectを深く理解した上で、
本当にuseEffectを使用するべき場所なのかということを、
再度確認して慎重に使用していこうと思います。
ここまで読み進めていただきありがとうございます。