できない.dev

React で「Rendered more hooks than during the previous render」が解決できない

フックを条件分岐や早期 return の後で呼ぶと、レンダーごとに呼ぶ数が変わって出る。
フックは必ずコンポーネント先頭で無条件に呼ぶ。

公開:

要約

Rendered more hooks than during the previous render. は、あるレンダーでは呼ばれなかったフックが次のレンダーで呼ばれ、フックの呼び出し数がレンダー間で食い違ったときに出る。

React はフックを「毎回同じ順序・同じ数」で呼ぶ前提で状態を管理している。
条件分岐や早期 return の後にフックを置くと、この前提が崩れて壊れる。

よくある原因

  1. 早期 return の後のフック: if (!data) return null; の下に useStateuseEffect がある。
  2. 条件付き呼び出し: if (open) { useEffect(...) } のように、フックを分岐の中で呼んでいる。
  3. ループ内フック: 配列の length 次第で、呼ぶフックの数が変わる。

解決策

1. フックを先頭でまとめて呼ぶ

function Panel({ id }: { id: number }) {
  const [open, setOpen] = useState(false); // 先に全部呼ぶ
  const data = useData(id);
  if (!data) return null; // return はフックの後
  return <div>{open ? data.body : null}</div>;
}

フックはコンポーネント本体の先頭で、条件や return より前に無条件で呼ぶ。

2. 条件はフックの中に入れる

useEffect(() => {
  if (!open) return; // 呼び出しは常に、中身だけ条件分岐
  subscribe(id);
}, [open, id]);

「条件付きで副作用を走らせたい」場合は、フック自体は毎回呼び、内側で分岐する。

3. リストは子コンポーネントに切り出す

{items.map((item) => (
  <Row key={item.id} item={item} /> // 各 Row の中でフックを呼ぶ
))}

要素ごとにフックが要るなら、要素を別コンポーネントにして、その中で先頭フックとして呼ぶ。

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