🙋‍♂️

useEffectの使用法と考察

に公開

ReactのuseEffectについて、学習の記録とを兼ねてまとめてみます。
私なりの理解をなるべく言語化して、考察してみているので、参考にしてもらえたら嬉しいです。

筆者は2025年5月以降、Reactを本格的に学び始めたReact初心者です。
誤った情報を含む可能性もありますので、見つけた際は教えていただけると幸いです。

検証用プロジェクト

私が検証用で作成したプロジェクトです。
よろしければご利用ください。

https://github.com/kaze-wind-dev/practice-react-hook

1. useEffectの基本構造と用途

構文

useEffect(() => {
  // 副作用処理
  return () => {
    // クリーンアップ処理(必要であれば)
  };
}, [依存配列]);

用途

  • DOM操作(例:スクロールイベントの追加)
  • API通信(fetch)
  • タイマー(setInterval)
  • 外部ライブラリの初期化
  • クリーンアップ(removeEventListener、clearInterval など)

Reactでは純粋関数以外の処理は副作用(SideEffects)とされています。
DOM操作()やAPIの使用はすべてこの副作用となります。


実行のタイミング

  • マウント時にコールバック関数が実行される
  • 依存配列に渡した値や状態が更新されると再実行される
  • 再レンダリング時にはクリンナップ関数が一度され、再びコールバック関数が実行される
  • アンマウントされる際にクリーンアップ関数が実行される

依存配列に空の配列を渡すことは、1度限り実行することになるので、絶対に更新しないもの、再実行を必要としないもの場合には空でも問題はないが、React公式では原則として渡すことが推奨されているので、渡す方がよい。
そのため、絶対に更新しないものであればわたしてしまうのが安心。


2. タイマー制御の例

useEffect(() => {
  if (!timerRunning) return;

  const interval = setInterval(() => {
    setTimer((prev) => prev + 1);
  }, 1000);

  return () => clearInterval(interval);
}, [timerRunning]);

▶ 学んだポイント

  • setInterval のような「継続的副作用」には クリーンアップ関数の記述が必須
  • useEffect の依存配列に状態(例:timerRunning)を含めることで ON/OFF制御が可能
  • React開発環境では StrictModeの影響でuseEffectが2回実行されることがあるため、クリーンアップの重要性が高まる⇒2回実行することで不具合の発生を早めに検知できる

3. 非同期処理(fetch)の扱い方

基本形

useEffect(() => {
  const fetchData = async () => {
    const res = await fetch("https://example.com");
    const json = await res.json();
    setData(json);
  };

  fetchData();
}, []);

▶ 考察・理解

  • ReactはuseEffectの戻り値にPromiseを期待していない⇒リファレンスではundefindが戻り値になっているためPromiseが戻り値になるとエラーになる。
    • useEffect(async () => { ... }) は NG(戻り値がPromiseになり、Reactが誤動作・警告)
    • useEffect(() => { fetchData(); }) という形で、内部でasync関数を定義して実行する
  • 戻り値としてundefinedを維持することがReact的には正しい設計

4. async関数とawaitの関係

▶ 気づきと確認

  • async を付けた関数は、中に await がなくても Promise を返す
  • await を付ける理由は:
    • 関数の中で await を使いたいから(構文的要請)
    • 処理の順番を明示的に制御したいから
  • fetchData() のように async関数を呼んでも、その戻り値(Promise)を useEffectawait する必要はないということ

5. Reactにおけるトップレベルawaitの扱い

▶ 理解

  • 通常のJavaScript(ESMモジュール)では await をトップレベルで使える(Top-Level Await)
  • しかし、Reactのコンポーネント関数では使えない
  • JSXを返すべき関数が Promise を返すようになるため
  • Reactは 純粋関数的に描画関数を扱いたい
  • → 描画は同期的に、非同期は副作用の中で(useEffect)という構造が望ましい

6. 純粋関数との関係(自分の考察)

  • Reactはコンポーネントを「純粋関数(同じ入力→同じ出力)」として扱う前提で設計されている
  • async function Component() にして await fetch() する構造は、副作用と描画を混在させるためReactの哲学に反する
  • 副作用の発生タイミングと描画タイミングを明確に分ける必要がある
  • useEffect はこの「副作用のスコープ化」を行う手段

7. まとめ

  • useEffectは副作用を登録する場所であって、直接非同期関数を渡す場所ではない
  • async関数の実行結果(Promise)はuseEffectの外に返さなければ安全
  • クリーンアップ関数はイベントやリソースの開放に必須

useEffectや使い方について、ほとんどわかっていませんでしたが、完全ではなくとも結構理解することができたと思います。
自分用の記録ではありますが、誰かの参考になれば幸いです。


Discussion