React 中的函数式更新
React 中的函数式更新 ,指的是在更新 state 时,不给 setState 直接传新值,而是传一个函数 。这个函数会接收上一次最新的 state ,再基于它计算出新的 state。
基本写法
jsx
setCount(prev => prev + 1);
这里的:
prev:上一次最新的状态值prev + 1:基于旧值计算新值
为什么要用函数式更新?
因为 React 的状态更新可能是异步的、批量的。
如果你直接这样写:
jsx
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
你以为会加 3,实际上很多情况下只会加 1。
因为这 3 次拿到的 count 很可能都是同一个旧值。
而函数式更新:
jsx
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
React 会按顺序执行:
- 第一次:
0 -> 1 - 第二次:
1 -> 2 - 第三次:
2 -> 3
这样结果才是对的。
什么时候特别适合用?
1. 新状态依赖旧状态
最典型。
jsx
setAge(prev => prev + 1);
setList(prev => [...prev, newItem]);
2. 连续多次更新同一个状态
jsx
setCount(prev => prev + 1);
setCount(prev => prev + 1);
3. 异步回调里更新状态
比如:
setTimeoutPromiseuseEffect- 事件订阅回调
jsx
setTimeout(() => {
setCount(prev => prev + 1);
}, 1000);
这样更安全,因为你拿到的是最新状态,不是闭包里的旧值。
对象状态中的函数式更新
如果 state 是对象,常见写法是:
jsx
setState(prev => ({
...prev,
count: 10
}));
含义是:
- 先把旧对象展开
- 再覆盖你要修改的字段
例如:
jsx
const [user, setUser] = useState({
name: 'Tom',
age: 20
});
setUser(prev => ({
...prev,
age: 21
}));
结果:
jsx
{ name: 'Tom', age: 21 }
为什么不能直接改原对象?
jsx
user.age = 21;
setUser(user);
这样是直接修改原对象,引用没变,React 可能识别不到变化。
而函数式更新通常配合不可变写法:
jsx
setUser(prev => ({
...prev,
age: 21
}));
会返回一个新对象,React 更容易正确触发更新。
一句话理解
函数式更新就是:
"别相信你手里的旧 state,找 React 要最新的 state,再算出新值。"
什么时候用普通写法,什么时候用函数式更新?
普通写法
当新值不依赖旧值时可以直接写:
jsx
setTheme('dark');
setVisible(true);
函数式更新
当新值依赖旧值时,优先用:
jsx
setCount(prev => prev + 1);
setItems(prev => [...prev, item]);
总结
公式记住就行
- 不依赖旧状态:直接传值
- 依赖旧状态:传函数
最常见示例
jsx
setCount(prev => prev + 1);
setList(prev => [...prev, newItem]);
setState(prev => ({ ...prev, loading: false }));