useEffectの依存配列が正しくないケースを構造から見抜く
Reactの useEffect
をレビューする際、最も見落とされやすく、かつ深刻な影響を与えるのが「依存配列の不備」です。依存すべき値が記載されていない、あるいは不要な値が記載されている場合、レンダリングのタイミングや副作用の発火条件が意図と食い違い、バグの温床になります。
本記事では、useEffect
の依存配列に関する設計ミスを構造レベルで見抜く方法を、レビューアー視点で解説していきます。
技術背景:Reactの依存配列が担う構造的責任
useEffect
の第二引数は依存配列(deps
)と呼ばれ、Effectの実行タイミングを制御する役割を持っています。
- 空配列:初回マウント時にのみ実行される
- 値を含む配列:その値が変化するたびに再実行される
- 省略:すべてのレンダリング後に実行(非推奨)
依存配列とは、useEffect(fn, [dep1, dep2])
の形式で与える第二引数であり、Effect内で参照する「再実行のトリガー」となる変数群です。これが不完全だと、Reactの最適化ロジックに逆らい、意図しない副作用の実行が発生します。
React公式は依存配列を「副作用関数が参照しているすべての値を含めるべき」と明言していますが、現場ではこの原則が守られていないケースが非常に多いです。
よくある依存配列ミスとその構造的背景
ケース1:参照値が依存配列に含まれていない
useEffect(() => {
if (user.role === "admin") {
fetchAdminConfig();
}
}, []); // ← userが入っていない
@Reviewer: userオブジェクトを参照しているにもかかわらず、依存配列に含まれていません。これはメモリリークや古いデータ利用の原因になります。
構造的に見ると、このEffectは「user.role」という条件付き副作用を含んでおり、userという依存元と副作用の責務が乖離しているのが問題です。
ケース2:関数の依存を誤って省略している
useEffect(() => {
doSomething();
}, []); // ← doSomethingがuseCallbackでもメモ化されていない
@Reviewer: `doSomething` はuseEffect外で定義されていますが、再定義の可能性があるため依存配列に含めるべきです。useCallbackでメモ化するか、依存として明記してください。
このパターンは、「安定した参照」である前提が壊れた時点で副作用が不整合になるため、コードの可読性が高くても設計として脆弱です。
なぜレビューアーは構造で判断すべきか?
依存配列の誤りは、見た目の挙動ではすぐには分かりません。静的解析ツール(ESLintのreact-hooks/exhaustive-deps
)を導入していないプロジェクトでは特に、レビューで見抜くしか手段がありません。
構造的な判断基準は以下の通りです:
- Effect関数内にあるすべての外部参照は、明示的に依存に含める必要がある
- ただし、安定している参照(定数・useRefなど)は除外可能
- useCallbackやuseMemoに依存している関数は、それ自体が依存となる
実務でありがちな「依存の過少・過剰」例とレビュー方法
過少依存(依存の記載漏れ)
useEffect(() => {
if (isReady && flag) {
doSomething();
}
}, [isReady]); // ← flagが依存にない
@Reviewer: 条件判定に `flag` を用いているにも関わらず、依存配列に含まれていません。ロジックの分岐条件が一致しない可能性があります。
過剰依存(意味のない依存)
useEffect(() => {
console.log("Mounted");
}, [Math.random()]);
@Reviewer: `Math.random()` は再評価されるたびに新しい値になるため、意図せずEffectが毎回走る構造になります。依存に含めるべきではありません。
可視化:依存構造を図で見る
Hookの安定性と依存の関係
副作用関数内で利用する関数を useCallback
でメモ化していない場合、その関数は毎回新しく定義されるため、参照の変更によってEffectが再実行される原因になります。
const handleClick = () => {
console.log("clicked");
};
useEffect(() => {
doSomething(handleClick);
}, [handleClick]); // ← 無限ループの原因
@Reviewer: handleClickが毎レンダリングで再生成されているため、useEffectが毎回再評価されます。useCallbackで安定化させるか、useEffect外で定義して依存を解消してください。
依存関係の過不足をレビューで判断するコツ
レビュー時は、以下のように読み取ると依存漏れを見抜きやすくなります。
- Effect関数内のすべての外部変数参照を列挙する
- useRefの
.current
参照は依存に含めない(値は安定) JSON.stringify(obj)
などの比較が依存を混乱させる可能性があるので、比較ロジックの代替提案も検討する
まとめ:依存配列レビューの指摘観点
- 副作用が参照しているすべての変数・関数が依存配列に含まれているか?
- 安定していない関数が依存から漏れていないか?
- useRefなどの安定変数を誤って依存に入れていないか?
- 過剰な依存(毎回変わる値やランダム値)を記述していないか?
レビューアーは構造を読み解くスキルが問われます。依存配列はただの文法でなく、設計思想の反映であることを意識すると、より精度の高いレビューが可能になります。