22
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

useEffectを何となく使っていた自分が、React公式を読んで正しい使い方を学び直した

22
Posted at

以前、業務の中でStrict Mode環境においてuseEffectが二重に発火することが原因で不具合が発生しました。useEffectの中に書いていた処理が意図せず2回実行され、想定外の状態更新が起きていたのです。

この不具合を解消する過程で、自分がuseEffectを使うべきタイミングやStrict Modeとの付き合い方を理解できていなかったことに気づき、あらためてReact公式サイトを読み直しました。

本記事では、React公式の内容をもとに、useEffectを使うべきタイミングと、依存配列・Strict Modeとの付き合い方を整理します。

1. useEffectの役割

React公式では、useEffectはReactのコードの外にあるものとコンポーネントを同期するためのものと説明されています。
例えば、ブラウザAPI、DOM操作、イベント購読、ネットワーク接続、外部ライブラリなどがこれに当たります。

つまり、useEffectを使うかどうかの判断軸は、

「その処理は、外部システムとの同期か?」

で考えるのがシンプルです。

2. useEffectを使う場面

2-1. イベント購読の登録・解除

useEffect(() => {
  const handleResize = () => {
    console.log(window.innerWidth);
  };
 
  window.addEventListener("resize", handleResize);
 
  return () => {
    window.removeEventListener("resize", handleResize);
  };
}, []);

これは window という外部の仕組みと同期しています。
コンポーネントが表示されている間だけイベントを購読し、不要になったら解除する、という用途なのでuseEffectが適しています。

2-2. 外部ライブラリやDOMとの連携

タイマー、アニメーションライブラリのインスタンス管理などもuseEffectの出番です。
Reactのレンダリングだけで完結しない処理は、useEffectで扱うのが自然です。

2-3. 画面表示に応じたデータ取得

データ取得は、外部のネットワークとのやり取りであるため、Effectを使う代表例です。

useEffect(() => {
  let ignore = false;
 
  async function fetchData() {
    const response = await fetch("/api/users");
    const result = await response.json();
 
    if (!ignore) {
      setUsers(result);
    }
  }
 
  fetchData();
 
  return () => {
    ignore = true;
  };
}, []);

ignore フラグを使ったcleanupは、コンポーネントのアンマウント後にstateを更新してしまうのを防ぐためのパターンです。

ただし、React公式ではuseEffectでの直接的なfetchについて、フレームワーク組み込みのデータ取得機能やReact Query(TanStack Query)などのライブラリの利用も検討すべきとしています。競合状態の処理、キャッシュ、重複リクエストの抑制などを自前で実装する必要があり、実務では意外と考慮点が多いためです。

3. 依存配列の扱い

useEffectはデフォルトで毎回のレンダリング後に実行されます。そのため、多くの場合は依存配列を指定して「どの値が変わったときに再同期するか」を明示する必要があります。

依存配列を手動で削ったり、意図的にごまかそうとすると、かえってバグの原因になります。
React公式のESLintルール(exhaustive-deps)でも、Effectは依存している値に合わせて再同期されるべきだとされています。

依存配列についてのポイント:

  • [](空配列):マウント時に1回だけ実行し、アンマウント時にcleanupが走る
  • [value]value が変わるたびに再実行される
  • 依存配列なし:毎回のレンダリング後に実行される(通常は意図しない挙動になりがち)

4. Strict Modeでの二重実行

開発環境でStrict Modeが有効な場合、初回マウント時に setup → cleanup → setup の追加サイクルが走ります。
これは不具合ではなく、cleanupが正しく書けているかを確認するための意図的な挙動です。

つまり、Strict Modeで問題が起きるということは、そのuseEffectのcleanup処理に不備がある、あるいはそもそもuseEffectを使うべきでない処理を書いている、というサインです。

自分の場合は、二重に発火しても問題ないようにcleanupを見直すか、そもそもEffectでやるべき処理ではなかったのかを切り分けることで、不具合を解消できました。

5. まとめ

useEffectを使うかどうかの判断は、「外部システムとの同期が必要か?」というシンプルな問いに集約できます。
また、依存配列を正しく指定すること、Strict Modeでの二重実行に耐えられるcleanupを書くことが、useEffectを安定して使うためのポイントです。
次の記事では、useEffectを使わなくてよい場面についてReact公式を読んで整理します。propsやstateからの計算、イベントハンドラで完結する処理など、Effectに頼らない方がシンプルに書けるケースをまとめます。

6. 参考

22
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?