【解決】Maximum update depth exceeded:発生場所から更新ループを調べる
Maximum update depth exceededを、レンダー中・Effect・親子間の更新に切り分けて診断します。Too many re-rendersとの違い、再現の縮め方、修正後の確認手順を解説します。
制作・運営:React Guide 運営者
Maximum update depth exceeded は、更新が次の更新を呼び続けているときに手掛かりになるメッセージです。まず最初に出たエラーとコンポーネントのスタックを記録します。後続のメッセージだけを見ると、原因になった更新を見失うことがあります。
このページでは「どのuseEffectを直すか」より前の切り分けを扱います。依存配列の具体的な修正は useEffectの無限ループ診断にまとめています。React 19系を想定していますが、エラーの表示文言や表示場所は環境によって異なります。
2026-09-09訂正:以前はレンダー中の無条件更新とEffectからの更新を同じエラーとして説明していました。前者では
Too many re-rendersが出ることがあるため、発生場所を分けて説明します。根拠のない「8割は解決」といった表現も削除しました。
最初の30秒で記録すること
「画面を開くだけ」「クリック直後」「入力中」「特定のデータの取得後」のどこで始まるかを確認します。無操作で再現するなら初期表示やEffect、操作後ならイベントからの更新連鎖が候補になります。ただし、発生タイミングだけで原因を断定はできません。
コンソールから次の情報を控えてください。
| 記録するもの | 調査に使う理由 |
|---|---|
| 最初のエラー全文 | 類似するエラーを区別する |
| 自分のコンポーネント名と行番号 | ライブラリ内部より先に自分の更新処理を探す |
| 再現する操作 | 修正前後で同じ条件を比較する |
| Reactのバージョン・StrictModeの有無 | 開発時の追加実行と継続的な更新を分ける |
本番サイトで無限ループを試さず、ローカルの再現用画面で調べます。操作できないほど更新が続く場合は、問題の更新処理を一時的にコメントアウトして調査を進めます。
切り分け1:レンダー中に関数を実行していないか
// 問題:クリックを待たず、描画するたびに実行する
<button onClick={handleClick()}>追加</button>
// 修正:クリック時に実行する関数を渡す
<button onClick={handleClick}>追加</button>handleClick() の中でsetStateすると、レンダーが更新を呼び、その更新が再びレンダーを呼びます。このようなケースでは Too many re-renders が出ることがあります。TypeScriptなら、戻り値をイベントに渡している型エラーも確認してください。
引数を渡す場合は onClick={() => handleClick(item.id)} とします。初期値を準備したいだけなら、コンポーネント本体で毎回setStateせず、useState(initialValue) または純粋な遅延初期化を使います。
遅延初期化の関数は開発時のStrictModeで追加で呼ばれることがあるため、データ送信などの副作用を入れません。
切り分け2:Effectが自分の依存を更新していないか
// 原因を示す抜粋。実際に動かすと更新が止まらなくなる。
const [count, setCount] = useState(0)
useEffect(() => {
setCount(count + 1)
}, [count])矢印を言葉にします。「Effectがcountを変える → countが依存値なのでEffectが動く → countを変える」。この循環が確認できれば、初期化・クリック処理・外部との同期のどれを実現したいのかに戻って修正できます。
[] に変えるのは万能な修正ではありません。たとえば選択中のユーザーが変わったらデータを取得するEffectなら、ユーザーIDへの依存が必要です。依存を消してループだけ止めると、古いユーザーの情報を表示し続けます。
切り分け3:親子間で更新が戻っていないか
フォーム部品やテーブルを追加した後に起きる場合は、親から渡す値と子から返す変更通知を書き出します。
親のselectedIds
→ 子がローカルstateへコピー
→ 子のEffectがonChangeを呼ぶ
→ 親が新しいselectedIds配列を保存
→ 最初に戻る子に渡すオブジェクトが毎回変わるだけで、必ず無限ループになるわけではありません。子のEffectがさらにstateを更新して、親へ戻る経路があるかが調査の要点です。画面の表示だけを行う子に変更通知を返させず、実際の入力操作のときにだけ親へ通知する設計を検討します。
ライブラリ内部の関数がスタックに表示されても、まず自分が渡すpropsとコールバックを確認してください。原因を特定せずライブラリのバージョンを下げると、再現条件が隠れるだけの場合があります。
再現を小さくする順序
- エラーが出るページをローカルで開き、同じ操作で再現できる状態を作ります。
- 問題のコンポーネントに渡すAPIデータを、同じ形の小さな固定データへ置き換えます。ループが止まったら、通信後の更新とデータ参照を調べます。
- 原因候補のEffectやコールバックを1つずつ外します。一度に全部外すと、どの経路が原因だったか分かりません。
- 候補が絞れたら、その値がどこで作られ、どこで更新されるかを書き出します。
- 最小の例で修正し、元のデータ・子部品を戻して確認します。
ログを足す場合は、調査用のカウンターをstateにしないでください。計測そのものが追加更新を発生させると、現象を変えてしまいます。まず console.count('selection effect') などで呼び出し回数を確認します。ログの回数だけで原因が確定するわけではありません。
修正完了を判断する基準
エラーが消えたことに加えて、元の機能を確認します。選択を変えると表示が更新されるか、入力を素早く変えても古い結果が残らないか、画面を出入りしても処理が重複しないかを調べます。StrictMode下で初期処理が追加実行されても、更新が無制限に続かない状態が目標です。
再現例を他の人に渡すときは、必要な依存パッケージ、対象コンポーネント、再現操作、期待結果を含めます。APIキーや実ユーザーのデータは含めません。当サイトの記事が原因と思われる場合は、訂正窓口へ記事URLと再現手順をお知らせください。
次に読む
確認してみる
クリック時だけ起きる例と、ページを開くだけで起きる例を分けて記録する。修正後も本来の入力変更が反映されるか確認する。
同じテーマの教材
じゃんけんゲームを作ろう(勝ち負けを判定)
コンピュータとじゃんけんできるゲームを作る回。自分の手をボタンで選び、相手の手を乱数で決めて、勝ち・負け・あいこを判定する方法を小学生向けに解説します。
おみくじ&ガチャを作ろう(ランダムに挑戦)
ボタンを押すと結果がランダムに変わるおみくじとガチャを作る回。乱数の作り方とstate・配列を組み合わせて、引くたびに結果が変わるアプリを小学生向けに解説します。
ボタンを押すと変わる:stateとイベントを学ぼう
useStateとonClickで画面に動きをつける方法を小学生向けに解説。カウンターやいいねボタンを作り、条件によって見た目を変える書き方まで体験しながら学びます。