浅谈React中的setState的执行时序

    谈到React,就不得不提到React中最重要的一个API方法——setState,setState 在 React 中是经常使用的一个 API,但是它存在一些的问题经常会导致初学者出错,核心原因就是因为这个 API 是异步的。
    但其实setState也有同步执行的时候,假如setState出现在我们代码自定义的dom事件中以及在setTimeout中时,实际上setState是同步执行的:

bodyClick = () =>{
        this.setState({
            count:this.state.count+1,
        )}
       console.log(this.state.count);//理论上 我们在setState之后立即打印 是得不到最新结果的 但是这里可以拿到+1之后的count,代码是同步执行的。
    }
    componentWillMount(){
        document.body.addEventListener('click',this.bodyClick);
        setTimeout(()=>{
            this.setState({
                count:this.state.count+1,
            )}
           console.log(this.state.count);//理论上 我们在setState之后立即打印 是得不到最新结果的 但是这里可以拿到+1之后的count,代码是同步执行的。
        },10)
    }


看到这里不要迷糊,其实如果去深究setState的实现方式你就会发现这种情况并不奇怪,setState其实是被react特意设计成一种异步的实现方式,目的是为了避免dom的频繁渲染(就比如连续多次调用setState会进行合并一样,我们后边会说),其实setState是异步还是同步执行,react内部有一个判断标准,这个标准是判断执行setState代码时,是否处于batch update状态,isBatchingUpdates是否被激活:
setState主流程:


浅谈React中的setState的执行时序_第1张图片
 

哪些能命中batchUpdate机制:
1、生命周期(以及生命周期调用的函数)
2、React中注册的时间(和它调用的函数)
3、React可以管理的入口
哪些不能命中batchUpdate机制:
1、setTimeout setInterval等( 以及调用的函数)
2、自定义的DOM事件(和它调用的函数)
3、React管不到的入口

 

下面再来说一下连续执行多个setState的合并问题:

如果你一次调用了多个 setState ,那么结果可能并不如你期待的一样:

handle() {
  // 初始化 `count` 为 0
  console.log(this.state.count) // -> 0
  this.setState({ count: this.state.count + 1 })
  this.setState({ count: this.state.count + 1 })
  this.setState({ count: this.state.count + 1 })
  console.log(this.state.count) // -> 0
}

第一,两次的打印都为 0,因为 setState 是个异步 API,只有同步代码运行完毕才会执行。setState 异步的原因我认为在于,setState 可能会导致 DOM 的重绘,如果调用一次就马上去进行重绘,那么调用多次就会造成不必要的性能损失。设计成异步的话,就可以将多次调用放入一个队列中,在恰当的时候统一进行更新过程。

第二,虽然调用了三次 setState ,但是 count 的值还是为 1。因为多次调用会合并为一次,只有当更新结束后 state 才会改变,三次调用等同于如下代码:

Object.assign(  
  {},
  { count: this.state.count + 1 },
  { count: this.state.count + 1 },
  { count: this.state.count + 1 },
)

则其实setState的合并类似于Object的assign方法,合并是存在覆盖的,需要特别注意,如果需要连续调用,还能正确累加,则要用到React提供的preState来实现:

handle() {
  this.setState((prevState) => ({ count: prevState.count + 1 }))
  this.setState((prevState) => ({ count: prevState.count + 1 }))
  this.setState((prevState) => ({ count: prevState.count + 1 }))
}

这样执行完毕之后count就会得到我们预期的值:3;

你可能感兴趣的:(react,javascript)