State 更新
更新 State 看起来很简单,但新手最常见的 bug 也在这里:一次点击加不动、对象更新不生效、数组操作后 UI 没变化…… 这一章把“正确更新 State”讲清楚。
函数式更新:解决“快照”问题
在 State:组件的记忆 里你已经见过:同一渲染里读到的是快照。 这段代码能正确加 3:
TypeScript
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount((c) => c + 1);
setCount((c) => c + 1);
setCount((c) => c + 1);
}
return <button onClick={handleClick}>{count}</button>;
}什么时候必须用函数式更新?
- 一段逻辑里连续更新同一个 state 多次
- 更新依赖旧值:加一、追加、切换、累加、基于上一帧计算
更新对象:不要直接修改
下面是最容易写出来、也最容易出问题的写法:
TypeScript
const [person, setPerson] = useState({ name: 'Ada', age: 20 });
function handleBirthday() {
person.age = person.age + 1;
setPerson(person);
}为什么这是个坑
- 你把
person改成了“同一个对象”,React 很难判断“到底变没变”。 - 更糟的是:mutation 会让调试和回溯变得困难。
正确做法是创建一个新对象:
TypeScript
const [person, setPerson] = useState({ name: 'Ada', age: 20 });
function handleBirthday() {
setPerson({ ...person, age: person.age + 1 });
}更新数组:新增、删除、替换
TypeScript
const [items, setItems] = useState<string[]>(['A', 'B']);
function addItem() {
setItems([...items, 'C']);
}
function removeFirst() {
setItems(items.slice(1));
}
function updateFirst() {
setItems(items.map((x, i) => (i === 0 ? x + '!' : x)));
}小抄:常用不可变操作
- 新增:
[...items, newItem] - 删除:
items.filter(...) - 替换:
items.map(...) - 切片:
items.slice(...)
下一步
- 当 state 逻辑变复杂(多字段、多分支),考虑用 useReducer。
- 当你需要在不触发渲染的情况下保存某个值,考虑 Ref。
动手练习
把“普通更新”改成函数式更新,让一次点击能 +3。
🌐浏览器运行