DrillLab
练习Practice

动手做Get your hands on it

练习跟着课文走 —— 每节课尾都有本课的练习。这一页是全部练习的总库,想集中刷题的时候来。 每个练习都写清了它来自哪一节,卡住了就回去看那一节。Practice follows the lessons — every lesson ends with the exercises for that lesson. This page is the whole library, for when you want to drill in one sitting. Each exercise names the lesson it came from, so you can go back when you stall.

0 / 148个做对过you got right

练习Exercises

筛出 148 个练习 · 第 4 / 13 页。Showing 148 · page 4 / 13.
来自From Task 2 · Delete:点 Delete,该行按 id 被移除Task 2 · Delete: click Delete and that one row is removed by id · React 考试React exam
L2填空Fill the blanks补全删除逻辑Fill in the delete logic

三个空。第三个空是这道题唯一会绕人的地方。

Three blanks. The third one is the only part that trips people up.

TSXsrc/components/NoteManager/index.tsx3 个空3 blanks
1const handleDelete = (id: number) => {
2 setNotes((prev) => prev.((note) => note. id));
3};
把 3 个空都填上才能检查(还差 3 个)Fill all 3 blanks to check (3 to go)
来自From Task 2 · Delete:点 Delete,该行按 id 被移除Task 2 · Delete: click Delete and that one row is removed by id · React 考试React exam
L3写整块Write a block不看答案,自己写出 Task 2Write Task 2 yourself, without looking at the answer

一行代码的题。但要一次写对,不许用 push / splice。

One line of code. But get it right the first time, and without push or splice.

要求Requirements
  • 按 id 移除对应的那一条Remove the matching note by id
  • 其余笔记全部保留,顺序不变Keep every other note, in the same order
  • 必须用函数式更新Use a functional update
  • 不许修改原数组(不许用 splice)Do not change the original array (no splice)
  • 不许按 title 或下标比较Do not compare by title or by index
TSXsrc/components/NoteManager/index.tsx
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From Task 2 · Delete:点 Delete,该行按 id 被移除Task 2 · Delete: click Delete and that one row is removed by id · React 考试React exam
L2Debug LabDebug LabDebug Lab · 删一条,同名的全没了Debug Lab · delete one note and every note with the same title goes too

测试全过,但手动测试时发现:三条标题相同的笔记, 点其中一条的 Delete,三条一起消失。

Every test passes, but a manual check shows this: with three notes that share a title, clicking Delete on one of them makes all three disappear.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
# 测试:4 passed (4) ← 测试全过! # 手动复现步骤: # 1. 添加 "会议记录 / 内容1" # 2. 添加 "会议记录 / 内容2" # 3. 添加 "会议记录 / 内容3" # 4. 点第 2 行的 Delete # 期望:只剩「内容1」「内容3」 # 实际:表格全空# Tests: 4 passed (4) ← every test passes! # Manual repro steps: # 1. Add "会议记录 / 内容1" # 2. Add "会议记录 / 内容2" # 3. Add "会议记录 / 内容3" # 4. Click Delete on the second row # Expected: only 内容1 and 内容3 are left # Actual: the table is empty
TSX有问题的 handleDeleteThe handleDelete with the bug示意Illustrative
1const handleDelete = (id: number) => {
2 const target = notes.find((n) => n.id === id);
3 setNotes((prev) => prev.filter((note) => note.title !== target?.title));
4};
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From Task 3 · Edit:回填、改文字、就地更新、退出编辑Task 3 · Edit: refill the form, change the button text, update the row where it is, leave edit mode · React 考试React exam
L2填空Fill the blanks补全编辑逻辑的四个关键位置Fill in the four key spots of the edit logic

四个空横跨两个函数。第 4 个空是最容易漏的那一行 —— 漏了它测试照样能过,但行为明显不对。

Four blanks across two functions. The fourth is the line people forget most often — without it the tests still pass, but the behavior is clearly wrong.

TSXsrc/components/NoteManager/index.tsx4 个空4 blanks
1const handleSubmitNote = (submittedNote: Note) => {
2 if (noteToEdit) {
3 setNotes((prev) =>
4 prev.((note) =>
5 note.id submittedNote.id ? : note,
6 ),
7 );
8 ;
9 } else {
10 setNotes((prev) => [...prev, submittedNote]);
11 }
12};
13
14const handleEdit = (note: Note) => {
15 setNoteToEdit(note);
16};
把 4 个空都填上才能检查(还差 4 个)Fill all 4 blanks to check (4 to go)
来自From Task 3 · Edit:回填、改文字、就地更新、退出编辑Task 3 · Edit: refill the form, change the button text, update the row where it is, leave edit mode · React 考试React exam
L3写整块Write a block不看答案,自己写出完整的 Task 3Write all of Task 3 yourself, without looking at the answer

handleEdithandleSubmitNote两个函数完整写出来(含 Task 1 的分支)。 这是 Q1 的完整答案,写对了这道题就通了。

Write both handleEdit and handleSubmitNote in full, including the Task 1 branch. This is the complete answer to Q1: get it right and the question is done.

要求Requirements
  • handleEdit:把这条笔记设为「正在编辑」,不要改动 noteshandleEdit: mark this note as the one being edited, and leave notes alone
  • handleSubmitNote 编辑分支:按 id 就地替换,位置和顺序不变handleSubmitNote, edit branch: replace by id in place, keeping the position and the order
  • handleSubmitNote 编辑分支:替换完要退出编辑模式handleSubmitNote, edit branch: leave edit mode once the replace is done
  • handleSubmitNote 新增分支:追加到末尾handleSubmitNote, add branch: append to the end
  • 全部使用函数式更新,不许修改原数组Use functional updates everywhere, and never change the original array
TSXsrc/components/NoteManager/index.tsx
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From Task 3 · Edit:回填、改文字、就地更新、退出编辑Task 3 · Edit: refill the form, change the button text, update the row where it is, leave edit mode · React 考试React exam
L3Debug LabDebug LabDebug Lab · 点 Update 之后毫无反应Debug Lab · nothing happens after you click Update

点 Edit,输入框正常回填,按钮变成 Update。 改完内容点 Update —— 列表一点变化都没有, 表单也没清空。控制台干净。

Click Edit and the inputs prefill correctly, and the button becomes Update. Change the content and click Update — the list does not change at all, and the form does not clear either. The console is clean.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
# 没有任何报错。 # 复现: # 1. 添加 "Old / c1" # 2. 点 Edit → 输入框显示 Old / c1,按钮变 Update ✓ 这两步正常 # 3. 把标题改成 "New",点 Update # 期望:列表里那条变成 New,表单清空,按钮回到 Add # 实际:列表还是 Old,表单还留着 New,按钮还是 Update # 测试结果: # ✓ adds a note # ✓ submit button disabled when inputs empty # ✓ deletes a note # ✕ edits a note in place # Unable to find text content "New" in element [data-testid="notes-list"]# No error at all. # Repro: # 1. Add "Old / c1" # 2. Click Edit → the inputs show Old / c1, the button reads Update ✓ both fine # 3. Change the title to "New" and click Update # Expected: that row becomes New, the form clears, the button goes back to Add # Actual: the row is still Old, the form still holds New, the button still reads Update # Test results: # ✓ adds a note # ✓ submit button disabled when inputs empty # ✓ deletes a note # ✕ edits a note in place # Unable to find text content "New" in element [data-testid="notes-list"]
TSX有问题的 handleSubmitNoteThe handleSubmitNote with the bug示意Illustrative
1const handleSubmitNote = (submittedNote: Note) => {
2 const note = { ...submittedNote, id: Date.now() };
3
4 if (noteToEdit) {
5 setNotes((prev) =>
6 prev.map((n) => (n.id === note.id ? note : n)),
7 );
8 setNoteToEdit(null);
9 } else {
10 setNotes((prev) => [...prev, note]);
11 }
12};
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From 四个测试逐条读,以及它们的盲区The four tests read line by line, and what they fail to catch · React 考试React exam
L1认出来Spot it哪个实现能骗过全部四个测试但其实是错的Which implementation passes all four tests and is still wrong

下面哪个 handleDelete 能让四个测试全部通过, 但明显违反题目要求?

Which handleDelete below makes all four tests pass while clearly breaking what the task asks for?

先选一个选项Pick an option first
来自From 四个测试逐条读,以及它们的盲区The four tests read line by line, and what they fail to catch · React 考试React exam
L1认出来Spot it这个测试失败是因为什么Why this test fails

你写的 handleSubmitNote setNotes((prev) => [...prev, submittedNote]), 但自己加的测试报「找不到 My Title」。 测试代码是 userEvent.click(btn); expect(list).toHaveTextContent("My Title")。 最可能的原因?

Your handleSubmitNote is setNotes((prev) => [...prev, submittedNote]), but the test you added reports that My Title cannot be found. The test code is userEvent.click(btn); expect(list).toHaveTextContent("My Title"). What is the most likely reason?

先选一个选项Pick an option first
来自From 四个测试逐条读,以及它们的盲区The four tests read line by line, and what they fail to catch · React 考试React exam
L3写整块Write a block自己补一个测试,覆盖「按 id 删除」这个盲区Write a test of your own to cover the delete-by-id blind spotDrillLab 自出Written by DrillLab

现有测试测不出「按 id 删除」。写一个新测试: 添加两条同名笔记,删掉其中一条, 断言另一条还在。
提示:两条数据时页面上有两个 Delete 按钮,getByRole 会因为「找到多个」而抛错 —— 得用 getAllByRole

The existing tests cannot check the delete by id. Write a new test: add two notes with the same title, delete one of them, and assert that the other is still there.
A note: with two notes there are two Delete buttons on the page, and getByRole throws because it found more than one. Use getAllByRole.

要求Requirements
  • 添加两条 title 完全相同、content 不同的笔记Add two notes with exactly the same title and different content
  • 用 getAllByRole 拿到 Delete 按钮数组,点第一个Use getAllByRole to get the array of Delete buttons, then click the first one
  • 断言 notes-list 不再含「内容A」Assert that notes-list no longer holds 内容A
  • 断言 notes-list 仍然含「内容B」Assert that notes-list still holds 内容B
  • 所有 userEvent 调用都要 awaitPut an await on every userEvent call
TSXsrc/NoteManager.test.tsx(自己加的测试)
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 读题:三条要求,每一条都在指定一种写法Reading the question: three requirements, and each one decides how you write it · React 考试React exam
L1认出来Spot it如果参数改成 Promise 数组会怎样What happens if the parameter becomes an array of Promises

假设签名改成 runTasks(promises: Promise<T>[], limit: number), 调用方写 runTasks([task1(), task2(), task3()], 2)。 会发生什么?

Suppose the signature becomes runTasks(promises: Promise<T>[], limit: number) and the caller writes runTasks([task1(), task2(), task3()], 2). What happens?

先选一个选项Pick an option first
来自From 读题:三条要求,每一条都在指定一种写法Reading the question: three requirements, and each one decides how you write it · React 考试React exam
L1认出来Spot it为什么不能直接用 Promise.allSettledWhy Promise.allSettled on its own is not the answer

有人写 return Promise.allSettled(tasks.map((t) => t()))。 它满足几条要求?

Someone writes return Promise.allSettled(tasks.map((t) => t())). How many of the requirements does it meet?

先选一个选项Pick an option first
来自From 实现:worker pool(工人池)Building it: a worker pool, meaning a fixed number of workers sharing one queue · React 考试React exam
L2填空Fill the blanks补全 worker pool 的五个关键位置Fill in the five key spots of the worker pool

五个空。第 2 个和第 4 个是最容易写错的 —— 一个关系到「顺序」,一个关系到「任务到底有没有被启动」。

Five blanks. Numbers 2 and 4 are the ones most often written wrong: one decides the order, the other decides whether the task was started at all.

TSq2/taskRunner.ts5 个空5 blanks
1export async function runTasks<T>(
2 tasks: Task<T>[],
3 limit: number,
4): Promise<SettledResult<T>[]> {
5 const results: SettledResult<T>[] = new Array(tasks.length);
6 let nextIndex = 0;
7
8 const worker = async () => {
9 while (nextIndex tasks.length) {
10 const i = nextIndex;
11 nextIndex++;
12
13 try {
14 const value = await ;
15 results[] = { status: "fulfilled", value };
16 } catch (reason) {
17 results[i] = { status: "", reason };
18 }
19 }
20 };
21
22 const workerCount = Math.(limit, tasks.length);
23 const workers: Promise<void>[] = [];
24 for (let w = 0; w < workerCount; w++) {
25 workers.push(worker());
26 }
27 await Promise.all(workers);
28 return results;
29}
把 5 个空都填上才能检查(还差 5 个)Fill all 5 blanks to check (5 to go)