🛋️

React `useEffect`発火タイミング備忘録

2025/02/16に公開

🟡 背景

useEffectのクリーンアップ関数が思った通りに発火しなかったので、原因を整理するために自分なりにまとめました。
特に、Parent.tsxChild.tsxを表示・非表示(toggle)する際のuseEffect発火タイミングに混乱しました。

💡 問題のきっかけ:

  • startせずにtoggle → クリーンアップだけ発火し、本体は発火しない。
  • start後にtoggle → 本体とクリーンアップの両方が発火する。

これにより、「toggleでアンマウントするならuseEffect本体も発火するはずでは?」という疑問がありました。
そこで、3パターンの想定でuseEffectの処理の流れをまとめてみました。

💻 今回使用するコード(ParentChild

Parent.tsx (親コンポーネント)

const Parent = () => {
  const [show, setShow] = useState(true);
  return (
    <>
      <button onClick={() => setShow(prev => !prev)}>toggle</button>
      {show ? <Child key={show ? "visible" : "hidden"} /> : null}
    </>
  );
};

Child.tsx (子コンポーネント)

useEffect(() => {
  alert("useEffect開始");
  if (timerState !== "active") return;
  
  const time = setInterval(() => setCount(prev => prev - ONE_SECONDS), ONE_SECONDS);
  return () => {
    alert("アンマウント");
    clearInterval(time);
  };
}, [timerState]);

💡 タイマー機能の発火フロー

1. ページにアクセス(初回マウント)

  • useEffect本体発火 → alert("useEffect開始")
  • クリーンアップはif (timerState !== "active") return;で発火せず。

2. startを押す(timerStateが変化)

  • useEffect再発火 → alert("useEffect開始")
  • クリーンアップが登録される。

3. stopを押す(timerStateが変化)

  • クリーンアップ発火 → alert("アンマウント")
  • 依存値変化でuseEffect再発火 → alert("useEffect開始")

💡 toggleでの表示・非表示フロー

1. 初回マウント(show = true

  • useEffect発火 → alert("useEffect開始")
  • クリーンアップはtimerStateの条件分岐により発火せず。

2. toggleで非表示(アンマウント)

  • クリーンアップ発火 → alert("アンマウント")
  • 本体は条件分岐により発火しない。

3. toggleで再表示(再レンダー)

  • useEffect再発火 → alert("useEffect開始")

💡 start後にtoggleした場合

1. 初回マウント:

  • useEffect発火 → alert("useEffect開始")

2. startを押す:

  • useEffect再発火 → alert("useEffect開始")
  • クリーンアップ関数が登録。

3. toggleで非表示:

  • クリーンアップ発火 → alert("アンマウント")
  • useEffect本体は条件分岐により発火しない。

🟠 発火タイミングまとめ表

タイミング useEffect本体 (alert("useEffect開始")) クリーンアップ (alert("アンマウント"))
初回マウント ✅ 発火 🚫 未発火
toggle (start前) 🚫 発火しない ✅ クリーンアップ発火
start ✅ 再発火 🚫 toggle時まで未発火
toggle (start後) 🚫 発火しない ✅ クリーンアップ発火

🔵 ポイントまとめ

  • useEffect依存配列の値が変わらない限り再発火しない
  • toggleアンマウント扱いでクリーンアップのみ発火する。
  • 依存配列が空の場合は**toggleで再表示時にuseEffectが再発火**する。

🟣 最終まとめ

  • 依存配列に変更がなければuseEffectは再発火しない。
  • toggleはアンマウント扱いでクリーンアップだけ実行する。
  • start後は依存値変化で本体とクリーンアップが発火する。
  • 依存配列が空の場合はtoggleで再表示時に再発火する。

まとめた後に見てみると意外と納得のいく処理順序だったので、よく使うからこそしっかり理解できてよかったです。
何か違った認識があったり、参考になるものがあればコメントで教えていただけると助かります🙇‍♂️

Discussion