Debug Lab · React 十种典型故障Debug Lab · ten typical React failures
每一种都给真实报错(或真实的「没有报错」),你来判断、定位、修复、验证。Each failure comes with the real error message, or with the real silence. You decide what it is, find it, fix it, and check the fix.
这一页有什么On this page5
- 看到报错能先判断类型,再决定去哪个文件找See an error and first decide its type, then decide which file to open
- 认出「不报错」的那几类 bug 的特征症状Recognise the symptoms of the bugs that report no error at all
- 养成「改完必须跑一遍验证」的习惯Build the habit of running a check after every fix
- 把错误信息和根因建立稳定的对应关系Build a reliable link between an error message and its root cause
考场上大部分时间不是在写新代码,是在查为什么不对。会读报错的人和不会读的人,同样的知识水平能差出一倍速度。During the exam most of your time is not spent writing new code. It is spent finding out why the code is wrong. With the same knowledge, someone who reads error messages well works about twice as fast as someone who does not.
react-notes-app/src/所有故障都基于这个项目的真实代码Every fault is based on the real code of this project
react-notes-app/src/先分诊:这个报错属于哪一类Sort it first: which kind of error is this?
拿到报错的第一件事不是改代码,是归类。The first thing to do with an error is not to change code. It is to put the error in a category.
React 项目的故障基本就这五类。归对类,排查范围立刻缩小:
| 类别 | 典型信号 | 去哪找 |
|---|---|---|
| 模块 / 路径 | Failed to resolve import、Cannot find module | import 语句、文件是否存在、大小写 |
| 类型 | TS2345 / TS2339 / TS2322 | 类型定义文件、props 接口 |
| 渲染循环 | Maximum update depth exceeded、页面卡死 | useEffect 依赖数组、onClick 是否被立刻调用 |
| 状态更新 | 没有报错,但界面不动 | 是否改了原对象(push / splice / 直接赋值) |
| 测试查询 | Unable to find an element / found multiple | testid 拼写、元素是否存在、await 是否漏了 |
最难的是第四类 —— 没有报错的那一类。它的特征是「console.log 数据是对的, 但屏幕上没反应」。看到这个组合, 直接去查有没有修改原对象。
Faults in a React project come in five kinds. Get the kind right and the search space shrinks immediately:
| Kind | Typical signal | Where to look |
|---|---|---|
| Module / path | Failed to resolve import, Cannot find module | The import statement, whether the file exists, letter case |
| Types | TS2345 / TS2339 / TS2322 | Type definition files, props interfaces |
| Render loop | Maximum update depth exceeded, the page freezes | The useEffect dependency array, whether onClick is called immediately |
| State update | No error, but the UI does not move | Whether the original object was mutated (push / splice / direct assignment) |
| Test query | Unable to find an element / found multiple | testid spelling, whether the element exists, whether an await is missing |
The fourth kind is the hard one — the one with no error. Its signature is “console.log shows the right data, but nothing happens on screen”. See that combination and go straight to looking for a mutated original object.
「不报错」的四种 bug,记住它们的症状Four bugs that report no error: learn their symptoms
| 症状 | 根因 |
|---|---|
| 数据变了,界面不动 | 改了原数组/对象(push、splice、直接赋值) |
| 组件完全不显示,控制台干净 | 组件名小写开头,被当成 HTML 标签 |
| 列表空白,但数据有值 | map 回调用了花括号却忘了 return |
| 点了「更新」毫无反应 | 匹配用的 id 被改过,map 一条都匹配不上 |
这四种在前面的课里都各自练过一次。 下面的练习是把它们放在一起,不告诉你是哪一种。
| Symptom | Root cause |
|---|---|
| The data changed, the UI did not | The original array or object was mutated (push, splice, direct assignment) |
| The component does not show at all, console is clean | The component name starts lowercase, so it is treated as an HTML tag |
| The list is blank although the data has values | The map callback uses braces and forgets to return |
| Clicking Update does nothing | The id used for matching was changed, so map matches nothing |
Each of these four was practised once in an earlier lesson. The exercises below mix them together without telling you which is which.
改完必须验证 —— 而且要验证到题面要求那一层Always check a fix, and check it against what the question asked for
这个项目的验证有两层,缺一不可:
npx vitest run—— 及格线。 4 个测试全过说明没有低级错误。npm run dev+ 手动三个场景 —— 真正的正确性。 加三条同名笔记删中间那条(验「按 id」)、 编辑中间那条(验「原位置」)、 更新完看按钮是否回到 Add(验「退出编辑模式」)。
Q2 那边的验证是 npm run q2, 盯 running now 不超过 limit、 最终顺序与输入一致、失败的那条以 rejected 出现。
Verification in this project has two layers, and you need both:
npx vitest run— the pass mark. 4 tests green means there are no basic mistakes.npm run devplus three manual scenarios — actual correctness. Add three same-named notes and delete the middle one (checks “by id”), edit the middle one (checks “in place”), and after an update see whether the button is Add again (checks “leave edit mode”).
Over on the Q2 side the check is npm run q2: watch that running now never exceeds limit, that the final order matches the input, and that the failing task appears as rejected.
动手做Get your hands on it
填空只是过渡。真正掌握的标准,是在没有答案的时候从头写出来 —— 所以做完 L2 之后一定要往 L3、L4 走。Filling blanks is a stepping stone. The real bar is writing it from nothing, so once L2 is comfortable, push on to L3 and L4.
新建了组件之后启动开发服务器,Vite 直接报错,页面白屏。
You add a new component, start the dev server, and Vite reports an error right away. The page is blank.
重构时把父组件传的 prop 名改了,子组件忘了跟着改。 页面能显示,但点 Delete 直接崩。
During a refactor the prop name passed by the parent was changed, and the child component was not changed to match. The page still renders, but clicking Delete crashes it.
代码看起来完全正确,手动点也没问题,但两个测试挂了。
The code looks entirely correct and clicking through it by hand works, but two tests fail.
这一题不告诉你是哪一类。控制台干净,console.log 显示数据是对的。 自己分诊。
This one does not tell you which category it is. The console is clean, and console.log shows the data is correct. Sort it yourself.
换一道题也能用Works on other problems too
考试不会原题重考。真正能带走的是「看到这种信号 → 伸手去拿这个解法」。The exam will not reuse the same question. What you take away is the reflex: see this signal, reach for that solution.
- 先分诊后动手:模块路径 / 类型 / 渲染循环 / 状态更新 / 测试查询。Sort the error before you touch anything: module path, type, render loop, state update, or test query.
- 「不报错」的 bug 靠症状识别,其中最常见的是「改了原对象」。Bugs with no error message are found by their symptoms, and the most common one is changing the original object.
- Testing Library 失败时会打印整个 DOM —— 在里面搜你期望的 testid。When Testing Library fails it prints the whole DOM. Search that output for the data-testid you expected.
- 编译期报错比运行时报错更精确,先修编译期的。A compile-time error is more precise than a runtime one, so fix the compile-time errors first.
- 验证要到题面那一层:测试过 ≠ 做对,还得手动跑三个场景。Check your work against the question, not against the tests: passing tests do not mean it is right, so run the three cases by hand.