一个前端,爱跑步、爱吉他、爱做饭、爱生活、爱编程、爱南芳姑娘,爱我所爱。世间最温暖又无价的是阳光、空气与爱,愿它们能带你去更远的地方。

  • 文章
  • 心情
  • 照片墙
  • 留言板
  • 工具
  • 友链
  • biaoblog

    专注web开发技术分享

    setState()异步 同步

    技术 138 2021-06-30 09:49

    上上篇博客我讲了setState() 的批处理合并,而setState()是异步的还是同步的,和setState() 的批处理有很大的关系,推荐先看完上上篇博客再来看这篇,会清晰很多

    地址如下:http://biaoblog.cn/#/bookInfo/1625016158478

    setState()批处理


    问题:setState()是异步的还是同步的?

    先看异步的情况:

    import React, { Component } from 'react';
    
    class com2 extends Component {
      state = {
        num: 0
      }
    
      add = () => {
        this.setState({
          num: this.state.num + 1
        })
        console.log(this.state.num)
    
        this.setState({
          num: this.state.num + 2
        })
        console.log(this.state.num)
    
        this.setState({
          num: this.state.num + 3
        })
        console.log(this.state.num)
    
      }
    
      render() {
        return (
          <div>
            <button onClick={this.add}>按钮{this.state.num}</button>
          </div>
        );
      }
    }
    
    export default com2;
    

    结果如下:

    在这里插入图片描述

    在这里插入图片描述

    之前说过,react内部为了优化setState()的批处理,会对setState()进行合并,并且对相同属性的设置只保留最后一次的设置,所按钮上3是可以理解的,

    但是打印出三次0,那又是为啥?

    在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到一个updateQueue中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state,而是异步的。

    总结:

    所以说setstate本身是同步的,一旦走了react内部的合并逻辑,放入了updateQueue队列中就变成异步了,而代码中的函数是react控制的,内部会走合并逻辑,所以这里的setState 不但是合并的也是异步的,所以打印出三个0


    控制setState的同步和异步:

    上面说了,setState是异步的原因是因为走了react内部的合并逻辑,那只要能绕过react内部的合并逻辑,不让它进入到updateQueue中不就变成同步了吗?因为setState()本身就是同步的

    利用setTimeout绕过react内部的合并逻辑

    import React, { Component } from 'react';
    class com2 extends Component {
      state = {
        num: 0
      }
    
      add = () => {
    			//利用setTimeout绕过react的控制,不让setState()走合并逻辑
        setTimeout(() => {
          this.setState({
            num: this.state.num + 1
          })
          console.log(this.state.num)
    
          this.setState({
            num: this.state.num + 2
          })
          console.log(this.state.num)
    
          this.setState({
            num: this.state.num + 3
          })
          console.log(this.state.num)
        });
    
      }
    
      render() {
        return (
          <div>
            <button onClick={this.add}>按钮{this.state.num}</button>
          </div>
        );
      }
    }
    
    export default com2;
    

    结果如下:

    在这里插入图片描述

    在这里插入图片描述

    总结:

    异步的情况:

    由React控制的事件处理函数,以及生命周期函数调用setState时表现为异步 。大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等(合成事件中),这些事件处理函数中的setState都是异步处理的。

    同步的情况:

    React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval,ajax,promise.then内等 React 无法掌控的 APIs情况下,setState是同步更新state的,

    值得一提的是

    setState()可以接收一个对象外,还可以接收一个函数:


    区别:

    传递对象

    批处理,对相同变量进行的多次处理会合并为一个,并以最后一次的处理结果为准

    传递函数

    链式调用,React 会把我们更新 state 的函数加入到一个队列里面,然后,按照函数的顺序依次调用。同时,为每个函数传入 state 的前一个状态,这样,就能更合理的来更新我们的 state 了,该函数有两个参数

    prevState

    props

    这和我们前面理解的setState()异步执行不冲突


    可以理解为:

    本来我想拿到上一次setState() 执行完后的结果,需要使用一些特殊的方式,绕开合并逻辑,让setState() 保持本身的同步执行特性,代码如下:

    import React, { Component } from 'react';
    
    class com2 extends Component {
    
      state = {
        count: 0
      }
    
      add = () => {
        setTimeout(()=>{
          this.setState({
            count: this.state.count + 1
          })
          console.log(this.state.count)
          this.setState({
            count: this.state.count + 1
          })
          console.log(this.state.count)
          this.setState({
            count: this.state.count + 1
          })
          console.log(this.state.count)
        })
    
      }
    
      render() {
        return (
          <div>
            <button onClick={this.add}>按钮{this.state.count}</button>
          </div>
        );
      }
    }
    
    export default com2;
    

    点击后结果:

    在这里插入图片描述

    在这里插入图片描述

    而现在,在异步执行的情况下,我还想拿到上一次setState执行完后的结果,react给我们提供了方式,就是在setState()里面传入一个函数,代码如下:

    import React, { Component } from 'react';
    
    class com2 extends Component {
    
      state = {
        count: 0
      }
    
      add = () => {
        this.setState((state) => {
          // 重要:在更新的时候读取 `state`,而不是 `this.state`。
          return { count: state.count + 1 }
        });
        console.log(this.state.count)
        this.setState((state) => {
          // 重要:在更新的时候读取 `state`,而不是 `this.state`。
          return { count: state.count + 1 }
        });
        console.log(this.state.count)
        this.setState((state) => {
          // 重要:在更新的时候读取 `state`,而不是 `this.state`。
          return { count: state.count + 1 }
        });
        console.log(this.state.count)
    
      }
    
      render() {
        return (
          <div>
            <button onClick={this.add}>按钮{this.state.count}</button>
          </div>
        );
      }
    }
    
    export default com2;
    

    结果如下:

    在这里插入图片描述

    在这里插入图片描述

    在setState中传一个函数,能拿到上次setState执行完后的结果,但是不妨碍是异步更新的,可以看到打印的是0,这是react给我们提供的方便之处


    转载:https://blog.csdn.net/fesfsefgs/article/details/108036605

    上一篇:ViewUi的render

    下一篇:setState()批处理

    文章评论

    评论列表(0