Reactでコンポーネントを開発しているとき、画面が真っ白になり、コンソールに以下のようなエラーが表示されて頭を抱えたことはありませんか?
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. (エラー:再レンダリングが多すぎます。Reactは無限ループを防ぐためにレンダリング数を制限しています。)
このエラーは、文字通りコンポーネントの再レンダリング(再描画)が終わりなく繰り返される「無限ループ」が発生していることを示しています。Reactが親切にもブラウザがフリーズする前に処理を強制停止してくれた状態です。
この記事では、このエラーが発生する根本的なメカニズムから、よくある3つの原因パターン、そして具体的な解決コードまでを分かりやすく解説します。
1. なぜ「Too many re-renders」が発生するのか?
原因を深く理解するために、まずはReactが画面を更新する基本的な仕組み(ライフサイクル)をおさらいしましょう。
Reactコンポーネントは、以下のいずれかの条件を満たしたときに再レンダリングされます。
- State(状態)が更新されたとき
- Props(親から渡されるデータ)が変更されたとき
- 親コンポーネントが再レンダリングされたとき
「Too many re-renders」が発生するのは、主に「1. Stateの更新」がトリガーとなります。 レンダリングの処理(関数コンポーネントの実行)の中でStateを更新してしまうと、以下のような悪魔のサイクルが成立します。
- コンポーネントがレンダリングされる
- レンダリングの途中でStateを更新する処理が走る
- Stateが更新されたので、Reactが「再レンダリングしなきゃ!」と判断する
- 再びコンポーネントがレンダリングされる
- またStateを更新する処理が走る……(以下無限ループ)
これが、エラーの正体です。
2. よくある原因パターンと解決策(コード例付き)
このエラーが発生するシチュエーションは、大きく分けて3つのパターンしかありません。自分のコードがどれに当てはまるかチェックしてみましょう。
パターン①:イベントハンドラーに「関数を実行した結果」を渡している(最頻出)
もっとも多いのが、onClick などのイベントリスナーに、関数を渡すのではなく「その場で実行した結果」を書いてしまっているパターンです。
❌ 間違ったコードの例
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
// ✕ ボタンがクリックされた時ではなく、レンダリング時に実行されてしまう
return (
<div>
<p>カウント: {count}</p>
<button onClick={setCount(count + 1)}>加算</button>
</div>
);
}
💡 原因の解説
onClick={setCount(count + 1)} と書くと、Reactがこのボタンを描画する(レンダリングする)瞬間に、setCount が実行されてしまいます。 すると画面を表示する前にStateが変わり、すぐさま再レンダリングが走り、また実行され……となり、一瞬で無限ループが完成します。
⭕ 正しい解決コード
イベントハンドラーには、関数の実行結果ではなく「関数そのもの(またはアロー関数)」を渡す必要があります。
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
// 〇 アロー関数で包むことで、クリックされた時だけ実行されるようにする
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>加算</button>
</div>
);
}
引数がない場合は、あらかじめ定義した関数名をそのまま渡す形でもOKです(例: onClick={handleClick})。
パターン②:コンポーネントの直下(トップレベル)でStateを更新している
関数の外や、イベントハンドラーの中ではなく、コンポーネントの処理の途中で無条件に setState を実行しているパターンです。
❌ 間違ったコードの例
import { useState } from 'react';
export default function UserProfile({ isLoggedIn }) {
const [status, setStatus] = useState('guest');
// ✕ レンダリングのたびに条件評価され、高頻度でStateが更新される
if (isLoggedIn) {
setStatus('member');
}
return <div>ステータス: {status}</div>;
}
💡 原因の解説
このコンポーネントが実行されるたびに、if (isLoggedIn) の評価が行われ、setStatus('member') が呼び出されます。Stateが変わるとコンポーネントは最初から再実行されるため、再び if 文に捕まり、無限ループになります。
⭕ 正しい解決コード
初期値の段階で条件分岐させるか、あるいは「特定のデータ(Propsなど)が変わったときだけStateを同期する」ために useEffect を使用します。
import { useState, useEffect } from 'react';
export default function UserProfile({ isLoggedIn }) {
const [status, setStatus] = useState('guest');
// 〇 useEffectを使い、isLoggedInが変更された時だけ実行する
useEffect(() => {
if (isLoggedIn) {
setStatus('member');
} else {
setStatus('guest');
}
}, [isLoggedIn]); // 依存配列に監視したい変数を指定
return <div>ステータス: {status}</div>;
}
※なお、PropsからStateを単純に同期させるだけであれば、Stateを作らずに const status = isLoggedIn ? 'member' : 'guest'; と直接変数として計算する方が、Reactの本質的によりクリーンな設計になります。
パターン③:useEffectの依存配列(Dependency Array)の設定ミス
useEffect を使っているのに無限ループが起きる場合、第二引数である「依存配列」の設定に問題があります。
❌ 間違ったコードの例
import { useState, useEffect } from 'react';
export default function DataFetcher() {
const [data, setData] = useState([]);
useEffect(() => {
// 擬似的なデータフェッチ処理
const fetchedData = [{ id: 1, name: '商品A' }];
// ✕ 自身のStateを更新している
setData(fetchedData);
}, [data]); // ✕ 依存配列に更新対象の『data』を入れている
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
💡 原因の解説
useEffect は、「依存配列([] の中身)に変化があったとき」に実行されます。 上記のコードでは、useEffect の中で setData を行っているにもかかわらず、依存配列に data 自体を指定しています。
dataが変わる ➔ 2.useEffectが動く ➔ 3.setDataでdataが変わる ➔ 4. 依存配列が変化を検知して 1 に戻る、というループに陥ります。
⭕ 正しい解決コード
コンポーネントのマウント(初回表示)時に1回だけ実行したい場合は、依存配列を空の配列 [] にします。
import { useState, useEffect } from 'react';
export default function DataFetcher() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchedData = [{ id: 1, name: '商品A' }];
setData(fetchedData);
}, []); // 〇 空の配列にすることで、初回レンダリング時のみ実行される
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
3. エラーを未然に防ぐためのチェックリスト
今後「Too many re-renders」で開発の手を止めないために、以下のポイントを意識してコードを書きましょう。
- [ ]
onClickやonChangeに渡す関数に()がついて、その場で実行形になっていないか? - [ ] 引数付きの関数をイベントに渡すときは、
() => myFunction(arg)のようにアロー関数でラップしているか? - [ ] コンポーネントの関数直下(トップレベル)で
setSomething()を呼び出していないか? - [ ]
useEffectの中で更新しているStateを、そのまま依存配列(第二引数)に含めてしまっていないか?
まとめ
Reactの「Too many re-renders」エラーは、一見すると深刻に見えますが、原因は「State更新タイミングのコントロールミス」が100%です。
「どこでStateが書き換わり、どのタイミングでレンダリングが走るのか」というデータの流れを意識することで、驚くほど簡単に解決・予防できるようになります。バグを恐れず、クリーンで高速なReactアプリケーションを構築していきましょう!


コメント