React useEffect無限ループの原因3つと解決法

問題

コンポーネントがレンダリングされた途端、useEffectが止まらず繰り返し実行されます。コンソールにログが大量に出力され、APIが何百回も呼び出されていました。

解決方法

無限ループの原因はほぼ3つです。

1. 依存配列の省略

// 無限ループ - 毎回のレンダリングで実行
useEffect(() => {
  fetchData();
});

// 解決 - マウント時に一度だけ実行
useEffect(() => {
  fetchData();
}, []);

2. オブジェクト・配列を依存配列に入れた場合

// 無限ループ - 毎回のレンダリングで新しいオブジェクトが生成される
const options = { page: 1, limit: 10 };

useEffect(() => {
  fetchData(options);
}, [options]); // 参照が毎回変わるため無限実行

// 解決 - useMemoで参照を固定
const options = useMemo(() => ({ page: 1, limit: 10 }), []);

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

3. useEffect内で直接stateを変更

// 無限ループ - state変更 → 再レンダリング → useEffect → state変更 → ...
useEffect(() => {
  setCount(count + 1);
}, [count]);

// 解決 - 条件文でガード
useEffect(() => {
  if (count < 10) {
    setCount(count + 1);
  }
}, [count]);

ポイント

  • 依存配列を空の配列[]にすると、コンポーネントのマウント時に一度だけ実行されます。ほとんどのデータフェッチングはこれで十分です。
  • オブジェクトや配列は毎回のレンダリングで新しい参照が生成されます。useMemouseCallbackで参照を固定するか、プリミティブ値のみを依存配列に入れてください。
  • ESLintのreact-hooks/exhaustive-depsルールの警告を無視せず、依存関係の構造を見直すことをお勧めします。