useEffectが無限ループになる
依存配列の設定ミスによる再実行の連鎖を防ぐ
❓ よくある現象
- ページが読み込まれた瞬間、useEffectが無限に呼ばれ続ける
- ブラウザが重くなる、APIが何度も呼び出される
- 開発ツールで「更新のたびに再レンダリング」が確認できる
🚨 典型的なミスの例
❌ NGコード:状態を更新しているのに依存配列に含めていない
const [count, setCount] = useState(0)
useEffect(() => {
setCount(count + 1) // ← これが state を更新 → 再レンダリング → 再実行 の無限ループに
}, [])
✅ 対策:
- 状態を更新する場合は、依存関係が変化しないような記述を避ける
- または
setState
をuseEffect
外で使う
💥 よくある原因まとめと対処
原因 | 説明 | 対策 |
---|---|---|
依存配列が空なのに setState() を使っている |
初回に実行 → 更新 → 再実行 の連鎖 | setState は useEffect 外で使う |
依存配列に毎回新しく生成されるオブジェクトを入れている | オブジェクトや関数の参照が毎回変わる | useMemo や useCallback でメモ化する |
依存配列の記述漏れ・不要な依存の追加 | 実際に変化しない値でも再実行のトリガーになっている | 依存配列を必要最小限にする(ESLintの警告も参考に) |
✅ 正しい例
const [data, setData] = useState([])
useEffect(() => {
fetch('/api/items')
.then((res) => res.json())
.then((json) => setData(json))
}, []) // 初回だけ実行される
⚙️ オブジェクトや関数を依存に入れるとき
❌ NG例
const filters = { category: 'all' }
useEffect(() => {
fetchItems(filters)
}, [filters]) // filtersは毎回新しいオブジェクトになるので再実行される
✅ 修正:useMemoでメモ化
const filters = useMemo(() => ({ category: 'all' }), [])
useEffect(() => {
fetchItems(filters)
}, [filters])
✅ まとめ:無限ループ対策ポイント
チェック項目 | 実施すること |
---|---|
useEffect 内で状態を更新しているか? |
その状態が再実行の原因になっていないか |
依存配列に毎回変わる値(関数・オブジェクト)を入れてないか | useMemo / useCallback を使う |
依存配列が適切か? | ESLint(exhaustive-deps )の指摘を確認する |
🔗 参考