R54第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

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 分别归到五类假设中的哪一类。

动手任务

打开本课实验区:

  1. 复现症状 A:记录复现步骤,把 key 改成稳定 id,回归验证勾选不再漂移。
  2. 复现症状 B:打开「序号守卫」开关,再走一遍同样的操作,读日志确认过期快照被丢弃。
  3. 复现症状 C:解释旧快照从哪来;把 broken 版改成函数式更新,验证徽标 +3。

常见错误

  • 不复现就开始改:改完无法验证,等于没修;先拿到稳定复现步骤。
  • 一次改多处:修好了不知道是哪个改动起效;单变量验证。
  • 只看 console 不看 DevTools:渲染快照、组件树层级、context 值在 DevTools 里一目了然。
  • 修完不留档:三个月后同类问题从零再查;五项档案是团队的复利。

检查与复盘

自查清单:

  • 每个修复都附稳定复现步骤,且修复后回归通过
  • 能把症状归到五类假设之一,说清证据链
  • 问题档案(症状/复现/根因/修复/回归)已写

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1故障定位中等

bug 现场:开发环境下控制台打印出两次「effect 挂载了」,接口也被请求了两次;切到生产环境只执行一次。最可能的解释与正确处理是?

function Page() {
  useEffect(() => {
    console.log('effect 挂载了');
    fetchInitialData();
  }, []);

  return <main>内容</main>;
}

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...