できない.dev

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 が安全装置として打ち切っています。

もっとも多いのは、イベントハンドラの渡し方ミスです。

よくある原因

  1. onClick={handleClick()} のように関数を即時実行で渡している。
    返り値(多くは undefined)がハンドラになり、レンダーのたびに handleClick が走る。
  2. コンポーネント本体で条件なしに setState を呼んでいる。
  3. 表示用の派生値を作るためにレンダー中で setState している。
  4. 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) のように条件で囲み、ループを断ち切ってください。

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