本章属于 React 教程的核心路线。建议先通读概念模型,再手写最小示例,最后用练习验证自己能否独立复现。
本章目标
本章理解 useState、onClick、事件处理、重新渲染、批处理、函数式更新,以及数组不可变更新。
阅读本章时,请把每个 API 放回“数据如何变成界面、用户操作如何改变数据”的主线中理解。React 的学习重点不是背诵语法,而是建立可预测的渲染模型。
- 识别本章概念在真实组件中的位置
- 能写出一个最小可运行示例
- 能解释常见错误为什么发生
概念模型
State 是组件自己的记忆。调用 setState 不会立即修改当前变量,而是请求 React 在批处理后用新状态重新渲染组件。
你可以把 React 程序看成一棵由组件组成的元素树。每次输入数据、状态或外部系统发生变化,React 会重新计算需要展示的 React element,再用高效的方式更新浏览器中的 DOM。
最小示例
计数器和待办列表展示了两类常见更新:基于旧值的函数式更新,以及用 map 返回新数组的不可变更新。
import { useState } from "react";
export function CounterList() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([{ id: 1, done: false }]);
return (
<>
<button onClick={() => setCount((value) => value + 1)}>{count}</button>
<button
onClick={() =>
setItems((oldItems) =>
oldItems.map((item) =>
item.id === 1 ? { ...item, done: !item.done } : item
)
)
}
>
toggle
</button>
</>
);
}
type LessonNote = { title: string; done: boolean };\nconst note: LessonNote = { title: 'React 教程', done: false };
规则与陷阱
规则的价值在于让多人维护同一个项目时仍能预测行为。下面这些限制看似细碎,实际都在保护组件的输入、输出和生命周期边界。
- 根据旧状态计算新状态时使用函数式更新。
- 不要 push、splice 或直接改对象属性,应该返回新数组或新对象。
- 事件处理函数传引用,除非需要传参才包一层箭头函数。
- 批处理会合并多次更新,不要依赖 setState 后的旧变量立刻变化。
练习
练习应当先小后大:先验证一个概念,再把它接入完整页面。不要在没有理解失败原因时复制更复杂的代码。
- 写一个加一和减一计数器。
- 用 map 切换列表项的完成状态。
- 连续调用三次 setCount,比较直接写法和函数式更新的结果。