初級8分で読める

【解決】Maximum update depth exceeded:発生場所から更新ループを調べる

Maximum update depth exceededを、レンダー中・Effect・親子間の更新に切り分けて診断します。Too many re-rendersとの違い、再現の縮め方、修正後の確認手順を解説します。

更新: 2026-09-12

制作・運営: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とコールバックを確認してください。原因を特定せずライブラリのバージョンを下げると、再現条件が隠れるだけの場合があります。

再現を小さくする順序

  1. エラーが出るページをローカルで開き、同じ操作で再現できる状態を作ります。
  2. 問題のコンポーネントに渡すAPIデータを、同じ形の小さな固定データへ置き換えます。ループが止まったら、通信後の更新とデータ参照を調べます。
  3. 原因候補のEffectやコールバックを1つずつ外します。一度に全部外すと、どの経路が原因だったか分かりません。
  4. 候補が絞れたら、その値がどこで作られ、どこで更新されるかを書き出します。
  5. 最小の例で修正し、元のデータ・子部品を戻して確認します。

ログを足す場合は、調査用のカウンターをstateにしないでください。計測そのものが追加更新を発生させると、現象を変えてしまいます。まず console.count('selection effect') などで呼び出し回数を確認します。ログの回数だけで原因が確定するわけではありません。

修正完了を判断する基準

エラーが消えたことに加えて、元の機能を確認します。選択を変えると表示が更新されるか、入力を素早く変えても古い結果が残らないか、画面を出入りしても処理が重複しないかを調べます。StrictMode下で初期処理が追加実行されても、更新が無制限に続かない状態が目標です。

再現例を他の人に渡すときは、必要な依存パッケージ、対象コンポーネント、再現操作、期待結果を含めます。APIキーや実ユーザーのデータは含めません。当サイトの記事が原因と思われる場合は、訂正窓口へ記事URLと再現手順をお知らせください。

次に読む

確認してみる

クリック時だけ起きる例と、ページを開くだけで起きる例を分けて記録する。修正後も本来の入力変更が反映されるか確認する。

仕様・手順の参照先

#react#error#useEffect#useState#beginner