React 项目调试
React 项目调试 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完你能用系统化的流程排查一个 React 综合故障:先稳定复现,再按「状态、闭包、身份、竞态、副作用」五类假设逐个收集证据,定位后留下可复现的问题档案;不再靠改代码碰运气。
问题场景
课程编辑页的线上反馈:「保存后列表偶尔显示旧标题;偶尔出现两行一样的课程;快速操作后徽标数字不对」。三个症状,四个嫌疑:竞态、key 错用、闭包过期、Effect 重跑——全都有可能,全都没有证据。乱改一通的代价是:改好一个,坏了两个,而且没人知道为什么好了。
核心解释
调试的骨架是先复现,再归类,每一步留证据。
**第一步:最小化复现。**把「偶尔」变成「必然」:什么操作序列、什么数据、什么网络条件能稳定触发?触发不了的 bug 修不了也验证不了——复现步骤本身就是调试产出的一部分。
第二步:按五类假设收集证据,一次验一个:
- 状态快照(R08):事件处理里连续 setState 后读值,读到的是旧快照?检查打印时机;
- 闭包过期(R30):定时器、订阅回调、异步回调里读到的 props/state 是注册那一刻的?给可疑回调打印值与时间戳;
- 身份不稳(R24/R35):依赖数组里的对象/函数每次渲染都是新引用,Effect 反复重跑?在 Effect 里记录执行日志;
- 竞态(R26):两次请求乱序,旧响应后到覆盖新结果?给每个请求编号,记录「发出 → 落地」的顺序;
let seq = 0;
function loadList() {
const n = ++seq;
getList().then((data) => {
if (n !== seq) return console.log("[丢弃] 第", n, "次结果过期");
setList(data);
});
}
- 副作用(R25):订阅、定时器没清理,卸载后仍在改状态?卸载后打印 still alive。
Effect 类问题最直接的证据是执行轨迹:
useEffect(() => {
console.log("[effect] run, keyword =", keyword);
return () => console.log("[effect] cleanup");
}, [keyword]);
**第三步:验证假设时只改一处,改完立刻用复现步骤回归。**修好了,把「症状 / 复现步骤 / 根因归类 / 修复方式 / 回归结果」五项写进问题档案——下次遇到同类症状,先翻档案。React DevTools 的组件树面板能直接查看任意组件当前的 props 与 state,它显示的是真实提交后的值,是证据收集的主工具,比散落的 console.log 更接近真相。
示例拆解
实验区是一个埋了三个真实 bug 的课程列表,每个症状都配了「证据日志」:
- 症状 A(身份类):每行有「已读」勾选框;给第 2 行勾上,点「插到最前」,勾选跑到别的课程头上——key 用了下标,行组件身份错乱;
- 症状 B(竞态类):改标题后点「保存」再立刻点「刷新」,界面最终显示旧标题——刷新快照在保存落地前拍摄,却在之后落地;事件日志里有编号证据;
- 症状 C(闭包类):点「批量标记 3 门课」,徽标只 +1——三次 setTimeout 都读同一个旧快照;函数式更新版对比就在旁边;
- 验证:逐个复现、读证据日志、把三个 bug 分别归到五类假设中的哪一类。
动手任务
打开本课实验区:
- 复现症状 A:记录复现步骤,把 key 改成稳定 id,回归验证勾选不再漂移。
- 复现症状 B:打开「序号守卫」开关,再走一遍同样的操作,读日志确认过期快照被丢弃。
- 复现症状 C:解释旧快照从哪来;把 broken 版改成函数式更新,验证徽标 +3。
常见错误
- 不复现就开始改:改完无法验证,等于没修;先拿到稳定复现步骤。
- 一次改多处:修好了不知道是哪个改动起效;单变量验证。
- 只看 console 不看 DevTools:渲染快照、组件树层级、context 值在 DevTools 里一目了然。
- 修完不留档:三个月后同类问题从零再查;五项档案是团队的复利。
检查与复盘
自查清单:
- 每个修复都附稳定复现步骤,且修复后回归通过
- 能把症状归到五类假设之一,说清证据链
- 问题档案(症状/复现/根因/修复/回归)已写
参考资料
- React 官方文档:React DevTools —— 组件树、props、state 的可视化检查
- React 官方文档:State 如同一张快照 —— 状态类 bug 的心智模型
- React 官方文档:使用 Effect 进行同步 —— Effect 类 bug 的证据来源
读完这一课了?
标记完成后,工作台会安排接下来的学习。