できない.dev

React で「Maximum update depth exceeded」が解決できない

レンダリングのたびに state 更新が走ると再レンダリングが連鎖し Maximum update depth exceeded になる。
ハンドラの即時呼び出し、render 中の setState、useEffect の依存設計を見直す。

公開: 更新:

実行例あり(2026-09-28 に実環境で検証)

要約

Maximum update depth exceeded は、レンダリングのたびに state 更新が発生し、再レンダリングが無限に連鎖 したときに React が止める警告。
原因はほぼ 3 つ — イベントハンドラの即時呼び出し、render 中の setState、useEffect の依存設計ミス。
どれも「更新を呼ぶタイミング」の問題に集約される。

実行例

node:20 のコンテナで jsdom 上に描画すると、Effect の中で更新した count を依存配列に入れた版は Maximum update depth exceeded. を出し続けながら 1 秒で 47955 回描画され、関数型の setState にして依存配列を空にした版は 2 回の描画で 1 に落ち着いた。onClick に setOpen(true) の呼び出し結果を渡した版は Too many re-renders. で止まり、関数を渡した版はクリックした後にだけ表示が opened に変わっている。

$ node bad_effect.js
console.error: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
1 秒後: 描画 47955 回 / 画面 "47954"
  同じエラーが 922 回: Maximum update depth exceeded. This can …
$ node solve_effect.js
1 秒後: 描画 2 回 / 画面 "1"
$ node bad_handler.js
console.error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
An error occurred in the <Modal> component.
 
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://react.dev/link/error-boundaries to learn more about error boundaries.
 
1 秒後: 描画 52 回 / 画面 ""
  同じエラーが 1 回: Too many re-renders. React limits the nu…
$ node solve_handler.js
クリック前: 描画 1 回 / 画面 "open"
1 秒後: 描画 2 回 / 画面 "opened"

— 2026-09-28 時点の出力

検証環境

検証日
実行環境
node:20Debian GNU/Linux 12 (bookworm)
バージョン
  • Node.js 20.20.2
  • npm 10.8.2
  • Python 3.11.2
  • Git 2.39.5

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. ハンドラの即時呼び出し: onClick={setOpen(true)} のように、関数参照ではなく実行結果を渡している
  2. render 中の setState: コンポーネント本体で直接 setState を呼び、毎レンダリングで更新が走る
  3. useEffect の自己再起: Effect 内で更新した state を依存配列に入れ、更新 → 再実行を繰り返す
  4. 参照の作り直し: 親が毎回新しい関数 / オブジェクトを子に渡し、子の更新が連鎖する

1 と 2 はどちらもレンダリングの最中に state を更新する形で、React 19 では Maximum update depth exceeded ではなく Too many re-renders. React limits the number of renders to prevent an infinite loop. で止まる(「Too many re-renders」の記事)。Maximum update depth exceeded が出るのは、主に 3 のように Effect の中の更新が連鎖する場合である。

解決策

1. ハンドラは渡す、呼ばない

// NG: レンダリング毎に実行され setState が走る
<button onClick={setOpen(true)}>open</button>
 
// OK: 関数参照を渡す
<button onClick={() => setOpen(true)}>open</button>

イベントに渡すのは「呼び出し方」であって「呼び出した結果」ではない。
考え方は 公式の Responding to Events(新しいタブで開く) を参照。

2. render 中に setState しない

// NG: 毎レンダリングで呼ばれ無限ループ
function App() {
  const [n, setN] = useState(0);
  setN(n + 1);
  return <p>{n}</p>;
}

初期値は useState の引数(必要なら遅延初期化)で与え、副作用は useEffect に閉じ込める。setState の挙動は useState 公式リファレンス(新しいタブで開く)。

3. useEffect の依存を切る

// NG: count を依存に入れると更新で再実行が連鎖
useEffect(() => {
  setCount(count + 1);
}, [count]);
 
// OK: 関数型にして count を依存から外す
useEffect(() => {
  setCount(c => c + 1);
}, []);

4. 子へ渡す参照を安定化する

const handleClick = useCallback(() => doIt(id), [id]);

毎レンダリングで新しい関数を作って子に渡すと、子側の更新が連鎖することがある。useCallback / useMemo で参照を固定し、不要な再レンダリングの連鎖を断つ。

この記事は役立ちましたか?