ReactのuseEffectで迷う本当の理由と正しい考え方

結論:useEffectで迷う原因は「ライフサイクル思考」と「依存配列の誤解」

Reactを学び始めてしばらくすると、多くの人が同じ壁にぶつかります。
それがuseEffectの使い方がよく分からない問題です。

  • 動いてはいるが、なぜ動くのか説明できない
  • ESLintに言われるがまま依存配列を書いている
  • 無限ループが起きて慌てて依存配列を空にする

こうした経験がある方は少なくないでしょう。

結論から言うと、useEffectで迷う最大の理由は次の2つです。

  • Reactを「ライフサイクルベース」で理解しようとしている
  • 依存配列を「実行タイミング指定」だと誤解している

この記事では、なぜuseEffectが分かりづらいのかを構造的に整理し、
実際に書きがちなコード例と、その結果どうなるかを交えながら解説します。

なぜuseEffectはこんなに分かりづらいのか

理由1:class時代のライフサイクルの代替だと思っている

Reactをclassコンポーネントから学んだ人ほど、useEffectをこう捉えがちです。

  • componentDidMount の代わり
  • componentDidUpdate の代わり
  • componentWillUnmount の代わり

確かに「結果」だけを見ると似ています。
しかし、考え方の起点がまったく違います。

classのライフサイクルは
「いつ実行されるか」を意識する仕組みでした。

一方でuseEffectは、
「どんな副作用が、どの値に依存しているか」
を宣言する仕組みです。

ここを取り違えると、useEffectは一気に難しくなります。

理由2:依存配列を実行タイミング指定だと思っている

次のコードは、非常によく見かけます。

useEffect(() => {
  fetchData();
}, []);

「空配列だから初回だけ実行される」
この説明自体は間違いではありません。

しかし本質は、
「この副作用は、どのstateやpropsにも依存していない」
とReactに伝えているだけです。

この理解がないまま、次のようなコードを書くと問題が起きます。

useEffect(() => {
  fetchUser(userId);
}, []);

userIdが変わっても再実行されません。
これは依存関係の宣言として誤っているからです。

実際によくある失敗例とその結果

失敗例1:とりあえず依存配列を空にする

useEffect(() => {
  setCount(count + 1);
}, []);

初心者がほぼ必ず通るコードです。

結果として、

  • countは1のまま
  • なぜ更新されないのか分からない

という状態になります。

これは、useEffect内で参照しているcountが
初回レンダリング時の値で固定されているためです。

失敗例2:ESLintに従って無限ループ

useEffect(() => {
  setCount(count + 1);
}, [count]);

今度は無限ループです。

これは
「countが変わる → effect実行 → countが変わる」
を繰り返しているだけです。

useEffect内で更新しているstateを、そのまま依存配列に入れるのは危険
という代表的なパターンです。

useEffectは「状態変化への反応」を書く場所

useEffectを理解するための最重要ポイントはこれです。

  • この副作用は、何が変わったら再実行されるべきか?

例えば、

  • userIdが変わったらユーザー情報を取得したい

のであれば、答えは明確です。

useEffect(() => {
  fetchUser(userId);
}, [userId]);

これ以上でも、これ以下でもありません。

副作用と計算処理を混同しない

次のようなコードは、useEffectを書く必要がありません。

const total = price * count;

これは副作用ではなく、単なる計算です。
useEffectに入れた瞬間、設計が歪みます。

useEffectが向いている人・向いていない人

useEffectが向いている人

  • Reactの再レンダリングの仕組みを理解している
  • 「何に依存しているか」で考えられる
  • 副作用と純粋な処理を分けて考えられる

このタイプの人にとって、useEffectは非常に強力な道具になります。

useEffectが向いていない人

  • 処理の実行順を厳密に追いたい
  • 命令的なコードで考えたい
  • JavaScriptのクロージャに慣れていない

この場合、無理にuseEffectを多用するとバグの温床になります。

実務で必ず知っておきたい注意点

useEffectの乱用は可読性を下げる

  • データ取得
  • 状態同期
  • フォーム初期化

これらを1コンポーネントに詰め込むと、
「なぜこの処理が動いているのか」が分からなくなります。

必要に応じて、

  • カスタムフックに切り出す
  • useMemo / useCallback を検討する

といった設計が重要です。

まとめ:結局どうすればいいか

useEffectで迷わなくなるために、意識すべきことは3つです。

  • useEffectはライフサイクルではない
  • 依存配列は「実行タイミング指定」ではない
  • 副作用だけを書く場所だと割り切る

この視点を持つだけで、
「よく分からないuseEffect」は確実に減ります。