【React】setState 的同步异步问题

setState 同步异步

在 React 中,setState 的执行时机依赖于它所在的执行环境。具体来说:

1)异步情况 :当 setState 在 React 的生命周期方法(如 componentDidMount、componentDidUpdate)或者合成事件(如捕获用户操作的事件处理函数)中被调用时,setState 是异步的,会自动进行批处理更新。这种异步性是为了提高性能,避免频繁渲染和批处理多个 setState 调用。

在 React 内部,调用 setState 后,新的状态并不会立即改变,而是被放入一个更新队列。这些更新会在某个时间点批量处理。为什么要这样做呢?这是为了优化性能和资源利用,防止频繁和冗余的组件重新渲染。

2)同步情况 :当 setState 在非 React 控制的环境中被调用,比如原生事件处理函数(如 addEventListener 注册的回调)或者定时器(如 setTimeout、setInterval)中被调用时,React 并不会对这些调用进行批处理,setState 是同步的。这是因为这些回调并不在 React 的合成事件系统或者生命周期管理之中。他们不受 React 的管理,React 无法确保可以安全地批处理这些更新。

3)实践中如何处理 setState:

  • 防止更新重复 :在合成事件和生命周期方法中,如果你需要基于前一个状态计算新的状态,应该使用 setState 的函数式调用形式:this.setState((prevState) => ({ ... })),确保状态计算的准确性。这也是因为解决可能批量更新导致的问题:无法获取状态更新中途的数据(因为批量更新只能控制到最新的状态)。这样的情况会每次更新后重新 render,否则都是在批量更新后 render 一次。
  • 确保更新顺序 :有时我们需要保证多次 setState 调用的顺序,可以将多个状态更新合并到一次调用中,或者利用 callback 参数来确保某个操作在状态更新后(组件重新渲染后)执行,一般用于获取最新值:this.setState(newState, callback)。

4)react Fiber 的影响 : 在新的 React Fiber 架构中,异步渲染提供了更精细的控制,使得 setState 的行为更加高效。Fiber 允许中断渲染和继续执行,更好地调配资源,这也是为什么 setState 的更新有时表现为异步。

setState 批量更新

React 的 setState 批量更新是异步的。

为了性能原因,React 会将多个 setState 调用合并成一次批量更新。具体过程如下:

1)React 先将调用的每个 setState 所产生的更新对象存储在一个队列中。

2)在所有的同步代码执行完之后,React 调度机制会统一处理这些队列中的更新,将多次 setState 进行合并(将多次状态改变完成后,再统一对 state 进行改变,然后触发 render),批量处理。

3)React 根据更新对象对组件进行合并更新,只在最后一次调用时实际渲染更新后的 DOM。

需要注意:

  1. setState 之所以设计成异步更新是为了保证性能,避免每次 setState 都引起组件的重新渲染。异步 setState ,合并多个状态更新统一处理,减少渲染次数,提高性能。

  2. react 使用事务机制实现批量更新,React 的批处理既包括状态更新(state),也包括生成并应用这些更新的 DOM 操作。

  3. setState 的第二个参数是一个回调函数,会在 setState 更新(因为批量更新,所以实际上是所有状态更新全部完成后才会执行这个回调函数)并重新渲染组件后被调用,所以这时我们可以获取最新值。

    js 复制代码
    this.setState({ counter: this.state.counter + 1 }, () => {
        console.log("State updated:", this.state.counter);
    });
  4. 强制同步更新,类组件可以通过 forceUpdate 强制同步更新,函数式组件使用 useSyncExternalStore 强制同步更新。

    1. 强制同步更新场景:如果组件的 render 方法是直接读取外部数据源时(而不是 render 中读取了 this.props, this.state 等,因为这样当你在组件或其任一父组件内调用 setState 时,它就将自动重新渲染),则必须告诉 React 在该数据源更改时更新用户界面。
  5. react18 之前,合成事件和生命周期方法中 setState 是批量更新的(异步的);原生事件或者 setTimeout 中,setState 是同步的。react18之后,默认情况下都是批量更新。

相关推荐
孔令飞43 分钟前
Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中指针引用Go中
前端
可乐鸡翅yeah_1 小时前
hls.js 缓冲区参数 maxBufferLength、maxBufferSize 通俗讲解与业务调参
开发语言·javascript·ios·音视频·safari·m3u8
溪语流沙1 小时前
【Web全栈进阶】PostgreSQL上手:Docker跑库 + 把早报站从SQLite迁过去
前端·docker·postgresql
IT_陈寒1 小时前
SpringBoot自动配置的坑:你以为的捷径可能是弯路
前端·人工智能·后端
paopaokaka_luck1 小时前
高校社团管理(AI辅助任务分配、协同过滤算法推荐、ECharts数据可视化、活动参与闭环、校园交流与反馈、器材借还管理)
java·前端·javascript·spring boot·数据分析·echarts
500841 小时前
React Native for OpenHarmony 实战:三方库 react-native-volume-control 的鸿蒙化适配指南
javascript·react native·react.js·electron·harmonyos
薛一半1 小时前
React-Redux三重优化实战揭秘
javascript·vue.js·react.js
Dovis(誓平步青云)1 小时前
导览音频切换太快,旧讲解不能覆盖新展品
android·前端·javascript·ecmascript·音视频·宠物
福兮说12 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理13 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程