エフェクトは依存値に基づいて再実行されますが、この配列を間違えることが React のバグの最大の原因です。
落とし穴 1:依存値の不足 → 古い値
エフェクトは、それが作成されたレンダリング時点の値をクロージャに閉じ込めます。依存値を省略すると、古い値を使い続けてしまいます:
jsx
useEffect(() => {
const id = setInterval(() => {
.(count);
}, );
(id);
}, []);
対処法:count を依存配列に追加するか、それを必要としない関数型アップデーターを使います(setCount(c => c + 1))。
オブジェクト・配列・関数は毎レンダリングで再生成されるため、毎回「新しい」ものと見なされ、エフェクトが落ち着くことはありません:
const options = { id }; // ❌ new object every render
useEffect(() => { load(options); }, [options]); // runs every render
対処法:プリミティブ値に依存させる([id])か、useMemo/useCallback でメモ化します。
useEffect(() => {
const ctrl = new AbortController();
fetch(url, { signal: ctrl.signal }).then(setData);
return () => ctrl.abort(); // cancels the old request when `url` changes
}, [url]);
クリーンアップがないと、遅い古いリクエストが新しいリクエストの後に解決されて新鮮なデータを上書きしてしまう(競合状態)可能性があり、サブスクリプションやタイマーも積み重なっていきます。react-hooks/exhaustive-deps の ESLint ルールが、依存値の不足のほとんどを検出してくれます。