← 中文资源

简体中文版本

React 重渲染过多 — 无限更新循环

通过不在渲染期间调用 setState、把更新移入 effect 或事件处理器,并打破依赖环,修复 “Too many re-renders”。

阅读英文原文

症状

本篇指南处理“React 重渲染过多 — 无限更新循环”。请从症状出发,按顺序执行诊断,在推进前确认每个假设;随后通过修复与验证步骤闭环。

可观察到的症状包括:

  • React 抛出 “Too many re-renders. React limits the number of renders to prevent an infinite loop”
  • 组件首次渲染正常,随后崩溃
  • CPU 飙高,标签页卡死

诊断

先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。

步骤 1

grep -n "set[A-Z].*(" src/components/*.jsx

在渲染路径中、处理器或 effect 之外调用的状态设置函数是首要嫌疑。

步骤 2

npx react-devtools # inspect render counts

React DevTools 会显示哪个组件无限重渲染及由哪个 hook 触发。

常见诱因(按出现频率排序):

  • 在渲染期间于组件体内直接调用 setState
  • effect 设置的状态同时在其依赖数组中,形成循环
  • 条件性 setState 因条件不稳定而在每次渲染都执行

根因

状态或属性变化都会触发 React 重渲染,但在渲染期间调用状态设置函数会在本次提交前再调度一次渲染,形成无界循环,React 遂以该错误中止。

修复

把状态更新移入事件处理器或 effect,修正误用的条件(例如用 onSubmit 而非 onSubmit()),并确保 effect 不依赖它自身设置的状态。

注意:为“修复” effect 而移除依赖数组会使其每次渲染都运行,可能重新引入循环。

// ❌ setState during render -> infinite loop
// function Counter({ initial }) {
//   const [n, setN] = useState(0);
//   setN(initial); // called every render
//   return <span>{n}</span>;
// }

// ✅ initialize once
function Counter({ initial }) {
  const [n, setN] = useState(initial);
  return <button onClick={() => setN((v) => v + 1)}>{n}</button>;
}

// ✅ effect that does not depend on the state it sets
useEffect(() => { setN(initial); }, [initial]);

验证

运行组件并确认只渲染一次且控制台无错误;React DevTools 应显示稳定的渲染次数。

修复后补充一条回归测试,复现原始症状,防止问题再次出现。

这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。