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 个练习 · 第 2 / 13 页。Showing 148 · page 2 / 13.
来自From ESM:import / export 与那些莫名其妙的报错ESM: import / export, and the errors that look strange at first · 地基 · 项目与语言Foundations · project and language
L2Debug LabDebug LabDebug Lab · ERR_MODULE_NOT_FOUNDDebug Lab · ERR_MODULE_NOT_FOUND

你在 node-subgraph/ 里跑 npm start, 服务器起不来。报错很长,但关键信息只有两行。

You run npm start inside node-subgraph/ and the server does not come up. The error is long, but only two lines of it matter.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
$ npm start node:internal/modules/esm/resolve:274 Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/me/node-subgraph/src/dataSources/orderDataSource' imported from /Users/me/node-subgraph/src/index.js at finalizeResolution (node:internal/modules/esm/resolve:274:11) Did you mean to import "./dataSources/orderDataSource.js"?
JavaScriptsrc/index.js(第 2 行有问题)src/index.js (line 2 is the problem)示意Illustrative
1import { resolvers } from './resolvers/orderResolvers.js';
2import { OrderDataSource } from './dataSources/orderDataSource';
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From 类型、type 与 interfaceTypes, type and interface · 地基 · 项目与语言Foundations · project and language
L2填空Fill the blanks补全 NoteTable 的 props 类型Fill in the props type of NoteTable

这是 NoteTable 真实的 props 类型。 三个空:一个数组类型、一个函数类型的参数、一个函数类型的返回值。

This is the real props type of NoteTable. Three blanks: an array type, a parameter of a function type, and the return value of a function type.

TSXsrc/components/NoteTable/index.tsx3 个空3 blanks
1export interface NoteTableProps {
2 notes: ;
3 onDelete: (id: ) => void;
4 onEdit: (note: Note) => ;
5}
把 3 个空都填上才能检查(还差 3 个)Fill all 3 blanks to check (3 to go)
来自From 泛型参数,以及怎么读 tsc 的报错Generic parameters, and how to read a tsc error · 地基 · 项目与语言Foundations · project and language
L1认出来Spot it这是谁的问题Whose problem is this

你刚 clone 好 react-notes-app,一行代码都还没写,先跑了npm run build,得到 10 个TS2582: Cannot find name 'test'。 同时 npx vitest run 显示 4 个测试全过。 最合理的判断是?

You have just cloned react-notes-app, written not one line of code, and run npm run build. You get 10 of TS2582: Cannot find name 'test'. At the same time npx vitest run shows all 4 tests passing. What is the most sensible conclusion?

先选一个选项Pick an option first
来自From 泛型参数,以及怎么读 tsc 的报错Generic parameters, and how to read a tsc error · 地基 · 项目与语言Foundations · project and language
L2填空Fill the blanks补全泛型参数Fill in the generic parameters

两处真实的泛型用法。想清楚「TypeScript 能不能自己推断出来」。

Two real uses of generics. For each one, work out whether TypeScript can infer it on its own.

TSX两个真实片段Two real snippets3 个空3 blanks
1// NoteManager:两个 state
2const [notes, setNotes] = useState<>([]);
3const [noteToEdit, setNoteToEdit] = useState<Note | >(null);
4
5// q2/taskRunner.ts:自定义泛型
6export type Task<T> = () => <T>;
把 3 个空都填上才能检查(还差 3 个)Fill all 3 blanks to check (3 to go)
来自From 组件就是一个返回界面的函数A component is a function that returns what you see on screen · React 考试React exam
L1认出来Spot it哪一行会把变量的值显示出来Which line prints the value of a variable

下面哪一行会在页面上显示这条笔记的标题内容?

Which line below shows the title of this note on the page?

先选一个选项Pick an option first
来自From 组件就是一个返回界面的函数A component is a function that returns what you see on screen · React 考试React exam
L1认出来Spot it三道题的代码该写在哪个文件Which file the code for the three tasks belongs in

Q1 的三个任务(Add / Delete / Edit)都要改动笔记列表。 这些逻辑主要写在哪个文件里?

All three tasks in Q1 (Add / Delete / Edit) change the note list. Which file holds most of that logic?

先选一个选项Pick an option first
来自From props:数据往下流,事件往上报props: data flows down, events go back up · React 考试React exam
L2填空Fill the blanks补全 NoteItem 的两个按钮Fill in the two buttons of NoteItem

这是 NoteItem 真实的两个按钮。 一个要传整条笔记,一个只传 id —— 想清楚各自要传什么, 以及怎么才能「点击时才执行」。

These are the two real buttons of NoteItem. One passes the whole note, the other passes only the id. Decide what each one has to pass, and how to make it run only on the click.

TSXsrc/components/NoteItem/index.tsx2 个空2 blanks
1<td>
2 <button onClick={ onEdit(note)} className="outlined">
3 Edit
4 </button>
5</td>
6<td>
7 <button onClick={() => onDelete()} className="danger">
8 Delete
9 </button>
10</td>
把 2 个空都填上才能检查(还差 2 个)Fill all 2 blanks to check (2 to go)
来自From props:数据往下流,事件往上报props: data flows down, events go back up · React 考试React exam
L2Debug LabDebug LabDebug Lab · 页面一打开,所有笔记就消失了Debug Lab · every note disappears the moment the page opens

添加两条笔记后刷新页面(假设有持久化),表格瞬间变空。 有时候浏览器还会卡住。先判断类型,再找病灶。

Add two notes, then reload the page (assume the notes are saved somewhere). The table goes empty at once, and sometimes the browser stops responding. First name the kind of error, then find the line that causes it.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
Warning: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. (另一种表现:没有任何报错,但表格永远是空的)Warning: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. (Another symptom: no warning at all, but the table stays empty forever.)
TSX有问题的 NoteItemThe NoteItem with the bug示意Illustrative
1const NoteItem: React.FC<NoteItemProps> = ({ note, onDelete, onEdit }) => {
2 return (
3 <tr>
4 <td>{note.title}</td>
5 <td>{note.content}</td>
6 <td>
7 <button onClick={onEdit(note)} className="outlined">Edit</button>
8 </td>
9 <td>
10 <button onClick={onDelete(note.id)} className="danger">Delete</button>
11 </td>
12 </tr>
13 );
14};
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From useState:让界面跟着数据变useState: making the screen follow the data · React 考试React exam
L1排顺序Order it把一次点击的顺序排对Put the steps of one click in order

用户点了某一行的 Delete 按钮。把下面五件事按发生顺序排好。

The user clicked the Delete button on one row. Put the five things below in the order they happen.

1React 重新执行 NoteManager 函数,拿到新的 JSXReact runs the NoteManager function again and gets new JSX
2NoteItem 的 onClick 触发,调用 onDelete(note.id)The onClick of NoteItem fires and calls onDelete(note.id)
3React 对比新旧 JSX,把变化的部分写进真实 DOMReact compares the new JSX with the old one and writes only the differences into the real DOM
4NoteManager 里的 handleDelete 执行,调用 setNotes(...)handleDelete inside NoteManager runs and calls setNotes(...)
5React 记下 notes 的新值,标记这个组件需要重新渲染React records the new value of notes and marks this component for a re-render
来自From useState:让界面跟着数据变useState: making the screen follow the data · React 考试React exam
L3写整块Write a block自己写出 NoteManager 的两个 state 和删除逻辑Write the two states of NoteManager and the delete logic yourself

只给你组件外壳。按要求补出两个 state 和 handleDelete。 不要看下面的答案,先自己写。

You get only the shell of the component. Add the two states and handleDelete as described. Write it yourself before you look at the answer below.

要求Requirements
  • 用 useState 声明 notes,类型是 Note[],初始值为空数组Declare notes with useState, typed Note[], starting as an empty array
  • 用 useState 声明 noteToEdit,类型是 Note | null,初始值为 nullDeclare noteToEdit with useState, typed Note | null, starting as null
  • handleDelete 按 id 移除对应笔记,必须用函数式更新,不许改动原数组handleDelete removes the matching note by id, using a functional update, without changing 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 受控输入:value + onChange 的闭环Controlled inputs: the loop between value and onChange · React 考试React exam
L2填空Fill the blanks补全受控输入的闭环Complete the loop of a controlled input

NoteForm 里 textarea 那一段补全。 三个空构成一个完整的闭环。

Fill in the textarea part of NoteForm. The three blanks together form one complete loop.

TSXsrc/components/NoteForm/index.tsx3 个空3 blanks
1const [content, setContent] = ("");
2
3<textarea
4 placeholder="Content"
5 value={}
6 onChange={(e) => setContent()}
7 data-testid="form-textarea"
8 className="form-textarea"
9/>
把 3 个空都填上才能检查(还差 3 个)Fill all 3 blanks to check (3 to go)
来自From 受控输入:value + onChange 的闭环Controlled inputs: the loop between value and onChange · React 考试React exam
L2Debug LabDebug LabDebug Lab · 点 Add 之后页面闪一下,笔记没了Debug Lab · the page blinks after Add and the note is gone

填好标题和内容,点 Add。页面明显闪了一下, 地址栏出现了 ?,表格还是空的。

Fill in a title and some content, then press Add. The page clearly blinks, a ? appears in the address bar, and the table is still empty.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
# 没有 JavaScript 报错。 # 现象:点击 Add 后 # - 页面整体刷新了一次 # - 地址栏从 http://localhost:5173/ 变成 http://localhost:5173/? # - 输入框被清空,表格依然是空的 # - React DevTools 里所有 state 都回到了初始值# No JavaScript error. # Symptom: after clicking Add # - the whole page reloaded once # - the address bar changed from http://localhost:5173/ to http://localhost:5173/? # - the inputs were cleared and the table is still empty # - in React DevTools every piece of state is back to its initial value
TSX有问题的 handleSubmitThe broken handleSubmit示意Illustrative
1const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
2 if (isFormInvalid) return;
3 const newNote = { id: Date.now(), title: title.trim(), content: content.trim() };
4 onSubmit(newNote);
5 setTitle("");
6 setContent("");
7};
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this