前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React 中setState更新state何时同步何时异步?

React 中setState更新state何时同步何时异步?

作者头像
Clearlove
发布2021-03-17 09:48:32
2.1K0
发布2021-03-17 09:48:32
举报
文章被收录于专栏:前端客栈前端客栈

React 中setState更新state何时同步何时异步?

先说结论

  • React控制的事件处理程序,以及生命周期内调用setState是异步更新state
  • React控制之外的事件中调用setState是同步更新state,比如原生js绑定事件、setTimeout/setInrerval等。

setState的“异步”并不是说内部由异步代码实现,本身的执行过程和代码都是同步的。

之所以会有一种异步方法的表现形式,归根结底还是因为React框架本身的性能机制所导致的。因为每次调用setState都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少re-render调用。

我们来看一些例子:

React封装事件

代码语言:javascript
复制
state = {
  number: 1
}

componentDidMount() {
  this.setState({ number: 3 });
  console.log(this.state.number); // 1
}

由此可见该事件处理程序中的setState是异步更新state的。

setTimeout

代码语言:javascript
复制
state = {
  number: 1
}

componentDidMount() {
  setTimeout(() => {
    this.setState({ number: 3 });
  }, 0);
  console.log(this.state.number); // 3
}

上面我们讲到,setState本身并不是一个异步方法,之所以会变现出一种异步的形式,是因为React框架本身的一种性能优化机制。那么基于这一点,假如我们能绕过React的机制,就可以令setState以同步的形式体现。

原生事件

代码语言:javascript
复制
state = {
  number: 1
}

componentDidMount() {
  document.body.addEventListener('click', this.resetState, false);
}

resetState() {
  this.setState({ number: 3 });
  console.log(this.state.number); // 3
}

同样的,原生事件也可以绕过React的性能优化机制,达到同步更新的表现。

React是如何控制异步和同步的?

ReactsetState函数实现中,会根据一个变量isBatchingUpdates判断是否直接更新this.state,还是放入队列中延时更新。

isBatchingUpdates默认是false,标识setState是同步更新this.state。但是有一个函数batchedUpdates会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会先调用这个函数将isBatchingUpdates修改为true。这样由React控制的事件处理过程setState就不会同步更新this.state

代码语言:javascript
复制
function enqueueUpdate(component){
  //injected注入的
  ensureInjected();
  //如果不处于批量更新模式
  if(!batchingStrategy.isBatchingUpdates){
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  //如果处于批量更新模式
  dirtyComponents.push(component);
}

事实上setState内部的执行过程是很复杂的,大致过程包括更新state,创建新的VNode,再经过diff算法对比差异,决定渲染哪一部分以及怎么渲染,最终形成最新的UI。这一过程包含组件的四个生命周期函数:

  • shouComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

如果是子组件并且依赖父组件,还会执行一个钩子函数componentWillReceiveProps

假如setState是同步更新的,每次更新这个过程都要完整执行一次,无疑会造成性能问题。事实上这些生命周期为纯函数,对性能还好,但是diff比较、更新DOM总消耗时间和性能吧。

在“异步”中如果对同一个值进行多次setStatesetState 的批量更新策略会对其进行覆盖,取最后一次的执行。

代码语言:javascript
复制
state = {
  number: 1
}

handleClick() {
  const number = this.state.number;

  this.setState({ number: number + 1 });
  this.setState({ number: number + 1 });
  this.setState({ number: number + 1 });
}

最终的结果只加了1。

如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。

代码语言:javascript
复制
hanldeClick() {
  this.setState({ name: 'Clearlove' });
  this.setState({ age: 18 });
}

hanldeClick处理程序中调用了两次setState,但是render只执行了一次。因为React会将多个this.setState产生的修改放在一个队列里进行批延时处理。

如何获取“异步”更新后的数据?

setState提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据。还是以刚才的例子做示范:

代码语言:javascript
复制
state = {
  number: 1
}

componentDidMount() {
  this.setState({ number: 3 }, () => {
    console.log(this.state.number); // 3
  });
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • React 中setState更新state何时同步何时异步?
    • 先说结论
      • React封装事件
      • setTimeout
      • 原生事件
    • React是如何控制异步和同步的?
      • 如何获取“异步”更新后的数据?
      相关产品与服务
      批量计算
      批量计算(BatchCompute,Batch)是为有大数据计算业务的企业、科研单位等提供高性价比且易用的计算服务。批量计算 Batch 可以根据用户提供的批处理规模,智能地管理作业和调动其所需的最佳资源。有了 Batch 的帮助,您可以将精力集中在如何分析和处理数据结果上。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档