关于React中的setState

React中的setState是异步的吗?

setState是同步执行的!但是state并不一定会同步更新(异步更新和同步更新都存在)

setState()中有个特别重要的布尔属性isBatchingUpdates(默认为false),它决定了state是同步更新还是异步更新。

异步更新:

  • 合成事件
  • 钩子函数

setState 只在合成事件和钩子函数中是“异步更新”的。异步更新的背后,是同步代码处理("合成事件和钩子函数"的调用在"更新"之前)。异步是为了实现批量更新的手段,也是React性能优化的一种方式。

同步更新:

  • setTimeout等异步操作中调用setState函数
  • DOM原生事件
  • 利用setState回调函数
  • 函数式setState用法

前两个都比较好理解,因为没有前置的batchedUpdate调用,所以isBatchingUpdates为false。不会开启批量更新模式,那么,在上面的调用栈图示里面,会直接走到事务更新。

后面两个方法,是React本身提供的。要注意的是,setState回调函数要在render函数被重新执行后才执行。

为什么有时连续两次 setState只有一次生效?

分别执行以下代码:

componentDidMount() {
    this.setState({ index: this.state.index + 1 }, () => {
      console.log(this.state.index);
    })
    this.setState({ index: this.state.index + 1 }, () => {
      console.log(this.state.index);
    })
  }

执行结果:

1
1
 
componentDidMount() {
    this.setState((preState) => ({ index: preState.index + 1 }), () => {
      console.log(this.state.index);
    })
    this.setState(preState => ({ index: preState.index + 1 }), () => {
      console.log(this.state.index);
    })
  }

执行结果

 
2
2

 

说明:

  • 1.直接传递对象的 setstate会被合并成一次
  • 2.使用函数传递 state不会被合并

 

测试输出

import React from "react";

class TestView extends React.Component {
  state = {
    count: 0,
  };
  componentDidMount() {
    this.setState({
      count: this.state.count + 1,
    });
    console.log("console: " + this.state.count);

    this.setState({ count: this.state.count + 1 }, () => {
      console.log("console from callback: " + this.state.count);
    });

    this.setState(
      (prevState) => {
        console.log("console from func: " + prevState.count);
        return {
          count: prevState.count + 1,
        };
      },
      () => {
        console.log("last console: " + this.state.count);
      }
    );
  }

  render() {
    console.log("render" + this.state.count);
    return <h4>test</h4>;
  }
}

export default TestView;

输出结果:

    // console: 0
    // console from func: 1
    // console from callback: 2
    // last console: 2

分析:

由于第一个和第二个setState是直接传递对象的,所以两次操作被合并成一次,然后由于这两次操作state是异步更新,此时的count还是0,输出 console:0

由于第二个setState是异步的,所以其回调也推入异步队列中。

执行第三个setState,此处为函数式调用setState,是同步更新的,此时取得prevState的count为上一次更新的count值1,此时输出 console from func: 1,并且执行count值+1变为2

第三个setState执行完毕,将其回调函数推入异步更新队列。

同步更新完毕,执行异步更新队列,先进先出,输出 console from callback: 2 ,最后 输出  last console: 2

 

 

总结

  • 钩子函数和合成事件中:

react的生命周期和合成事件中, react仍然处于他的更新机制中,这时 isBranchUpdate为true。

按照上述过程,这时无论调用多少次 setState,都会不会执行更新,而是将要更新的 state存入 _pendingStateQueue,将要更新的组件存入 dirtyComponent

当上一次更新机制执行完毕,以生命周期为例,所有组件,即最顶层组件 didmount后会将 isBranchUpdate设置为false。这时将执行之前累积的 setState

  • 异步函数和原生事件中

由执行机制看, setState本身并不是异步的,而是如果在调用 setState时,如果 react正处于更新过程,当前更新会被暂存,等上一次更新执行后在执行,这个过程给人一种异步的假象。

在生命周期,根据JS的异步机制,会将异步函数先暂存,等所有同步代码执行完毕后在执行,这时上一次更新过程已经执行完毕, isBranchUpdate被设置为false,根据上面的流程,这时再调用 setState即可立即执行更新,拿到更新结果。

 

参考文章:

由实际问题探究setState的执行机制
React中的setState是异步的吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值