React 中的函数式更新

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. 异步回调里更新状态

比如:

  • setTimeout
  • Promise
  • useEffect
  • 事件订阅回调
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 }));

转载:https://www.toutiao.com/item/7626739884350390830/

相关推荐
FungLeo4 天前
成为全栈·Next.js 网站前台篇·数据获取与缓存:60 秒再验证背后到底发生了什么
react·isr·next.js·成为全栈·react cache·opennext
liangshanbo121510 天前
React 性能瓶颈定位面试题——原理深挖版
react·performance·profiler
liangshanbo121510 天前
React 性能优化实战
性能优化·react
FungLeo10 天前
成为全栈·React 管理后台篇·按钮级权限:能力映射、菜单过滤与自锁保护
react·rbac·路由守卫·权限控制·前端权限·成为全栈
FungLeo10 天前
成为全栈·React 管理后台篇·评论审核工作流:把状态下拉改成可理解的动作
react·内容安全·状态机·交互设计·成为全栈·评论审核
liangshanbo121511 天前
React 状态持久化面试题——原理深挖版
react·状态持久化
liangshanbo121511 天前
Redux 的中间件(Middleware)的工作机制
中间件·react·redux
FungLeo11 天前
成为全栈·React 管理后台篇·Markdown 编辑器:预览、暗色主题与连续图片粘贴
图片上传·react·响应式设计·markdown编辑器·异步编程·成为全栈
FungLeo12 天前
成为全栈·React 管理后台篇·表单页范式:校验、数据回填与未保存保护
react·表单设计·zod·react hook form·成为全栈·数据回填
名字还没想好☜12 天前
React 文件上传实战:预览 URL 回收、多文件逐个进度与拖拽放置区踩坑
前端·react·next.js