目标
无限重渲染永远是一种形状:某条渲染路径调用 setState,其结果又重新触发同一条路径。Profiler 负责点名循环主角,console.trace 负责证明调用链,修复落在确切的关节上——不稳的依赖、不稳的标识、或本不该成为状态的派生值。断环之后用渲染计数器验证数字塌回个位数。
步骤要点
- 用 React DevTools Profiler 录两秒,火焰图里被重复绘成宽塔的那个组件就是循环主角。
- 对照三类经典环型自查:依赖里放了对象/数组字面量、父组件每次渲染新建函数传给 memo 子组件、派生值被存进 state 再用 effect 同步。
- 在可疑 effect 里加 console.count 与 console.trace,让每次触发都留下计数与调用栈,把怀疑变成证据。
- 在确切关节断环:更新器内部加守卫返回原引用、useCallback 稳定函数标识、useMemo 直接计算派生值替代存储。
- 组件里临时挂渲染计数器到 window,加载页面读数应稳定在 1-3 而不是每秒爬升,验证后删除计数器再合并。
排错提示
环涉及 Context Provider 时每次 value 变化全体消费者重渲染,用 useMemo 按 true 依赖钉住 value 对象,或把 context 拆成变化与稳定两半;只在 Strict Mode 双渲染出现的环是它替你揪出了渲染期副作用这种真实不纯,修不纯而不是摘掉 Strict Mode;环在第三方组件里就用 React.memo 加稳定 props 在边界隔离并提上游 issue,最后手段用 key 强制重挂,那是拿丢状态换停环。
适用边界
渲染计数是开发工具观测值,修复要在生产构建上复核,dev 开销可能放大或掩盖模式;到处加 memo 会累积复杂度债,优先修依赖与标识而非无脑 memo 化。
代码示例
the loop, named:
SettingsPanel renders: 3,412 in 2s
[loop] filter effect fired: 3,412
[loop] updater trace:
at SettingsPanel (SettingsPanel.tsx:41)
-> setFilter({...filter, ready: true})
-> filter identity changed
-> effect deps [filter] fired again
after the guard-in-updater fix:
renders: 2
effect fired: 1