React で「Too many re-renders」が解消できない(無限レンダリングループ)
「Too many re-renders. React limits the number of renders to prevent an infinite loop」は、レンダリング中に state を無条件で更新しているのが原因。
イベントに関数を即時実行で渡していないかをまず疑う。
#react#rerender#infinite-loop#hooks#setstate
公開:
要約
Too many re-renders. React limits the number of renders to prevent an infinite loop. は、レンダリングの最中に state を無条件で更新しているサインです。
更新がレンダーを呼び、そのレンダーがまた更新を呼ぶ――この往復が止まらず、React が安全装置として打ち切っています。
もっとも多いのは、イベントハンドラの渡し方ミスです。
よくある原因
onClick={handleClick()}のように関数を即時実行で渡している。
返り値(多くは undefined)がハンドラになり、レンダーのたびにhandleClickが走る。- コンポーネント本体で条件なしに
setStateを呼んでいる。 - 表示用の派生値を作るためにレンダー中で
setStateしている。 useEffectの依存配列を付け忘れ、毎レンダーで state を更新している。
解決策
1. ハンドラは「関数参照」で渡す
呼び出しの丸括弧を外し、引数が必要ならアロー関数で包みます。
// NG: レンダー中に実行されてしまう
<button onClick={handleClick()}>送信</button>
// OK: 関数そのものを渡す
<button onClick={handleClick}>送信</button>
// OK: 引数つきはアローで包む
<button onClick={() => handleClick(id)}>送信</button>2. レンダー中に setState しない
状態更新はイベントハンドラか useEffect の中だけで行います。
レンダー本体は「描く」だけに保ちます。
3. 派生値は計算で求める
ほかの state から導ける値は、新しい state にせず毎回計算します。
重いときだけ useMemo を挟みます。
const fullName = `${first} ${last}`; // setState しないどうしてもレンダー中に更新が要る稀なケースは、if (prev !== next) のように条件で囲み、ループを断ち切ってください。