壹 ❀ 引

了解react的同学都知道,react遵守渲染公式UI=Render(state),状态决定了组件UI最终渲染的样子(props也可以理解为外部传入的状态),由此可见state对于react的重要性。而在实际使用中,若我们想修改状态必须得借用APIsetState,也只有通过此方法修改状态才能顺利触发react下次render,那么对于一个使用如此高频的方法你了解它多少呢?

这里我们可以先抛出几个问题:

  • setState是同步还是异步?
  • 什么情况下同步?什么情况下异步?
  • setState批量合并是指只执行最后一次吗?比如执行了3次,第1,2次到底有没有执行?
  • 为什么要将setState设计成异步?这样设计的好处是什么?

贰 ❀ setState中的同步与异步

贰 ❀ 壹 updater为对象时的异步情况

setState接受一个带有形式参数的 updater 函数(也可能直接是一个对象)与一个回调callback(可选)。
官方明确表示,setState对于this.state并不是立刻更新,若在调用setState后想要立刻获取到最新的this.state,那么建议在setState的callback或者声明周期componentDidUpdate中获取,比如:

class Echo extends React.Component {
  state = {
    num: 1
  }

  componentDidUpdate() {
    console.log(this.state.num);//2
  }

  handleOnClick = () => {
    this.setState({ num: this.state.num + 1 }, () => {
      console.log(this.state.num);//2
    });
    console.log(this.state.num);//1
  }

  render() {
    return (
      <>
        <div>{this.state.num}</div>
        <button onClick={this.handleOnClick}>加1</button>
      </>
    )
  }
}

其实既然官方特意强调在callback中获取最新的this.state,那就已经说明存在某些地方拿不到最新的this.state的情况,比如上述代码中setState后我们立刻读取sum,可以发现num还是1,那么到这里我们可以得知setState对于this.state的更新确实是异步。

问题来了,react为什么将setState设计成异步呢?设想下我们有如下这种场景:

class Echo extends React.Component {
  state = {
    num: 1
  }

  componentDidUpdate() {
    console.log(this.state.num);//2
  }

  handleOnClick = () => {
    this.setState({
      num: this.state.num + 1
    }, () => {
      console.log(this.state.num)//2
    });

    this.setState({
      num: this.state.num + 1
    }, () => {
      console.log(this.state.num)//2
    });

    console.log(this.state.num);//1
  }

  render() {
    return (
      <>
        <div>{this.state.num}</div>
        <button onClick={this.handleOnClick}>加1</button>
      </>
    )
  }
}

当点击按钮,我们需要连着两次执行setState,那么react会帮我们修改两次this.state然后重新render两次吗?很明显并不是,react会批量合并多次setState操作,上述例子num最终是2,且render在点击后只会渲染一次。

React在开始重新渲染之前, 会有意地进行”等待”,直到所有在组件的事件处理函数内调用的 setState()都完成之后再做最终的this.state变更,这样可以通过避免不必要的重新渲染来提升性能。

贰 ❀ 贰 updater为函数时的异步情况

突然奇想,上述代码的需求有了些许变更,我们还是在点击后执行两次setState,但我预期最终的sum是3,如何做到呢?别忘了前面我们对于setState的语法介绍,本质上updater是一个接受最新state与最新props并用于返回你用来更新this.state的函数:

// 这里可以拿到最新的state与props,注意,是最新的state,而不是最新的this.state
(state, props) => stateChange

函数写法能让我们拿到立刻变更后的state,因此我们可以来看看这个例子:

class Echo extends React.Component {
  state = {
    num: 1
  }

  componentDidUpdate() {
    console.log('我是更新完成后的this.state',this.state.num);
  }

  handleOnClick = () => {
    this.setState((state, props) => {
      console.log('第一次调用,我是最新的state',state.num)
      console.log('第一次调用,我是当前的this.state',this.state.num)
      // 注意,这里用的是state,不是this.state
      return { num: state.num + 1 };
    }, () => {
      console.log('第一次调用,我是调用完成后的this.state',this.state.num)
    });

    this.setState((state, preProps) => {
      console.log('第二次调用,我是最新的state',state.num)
      console.log('第二次调用,我是当前的this.state',this.state.num)
      return { num: state.num + 1 };
    }, () => {
      console.log('第二次调用,我是调用完成后的this.state',this.state.num)
    });

    console.log('我用于检验异步,此时拿不到最新的this.state',this.state.num);//1
  }

  render() {
    console.log('用于检验render了几次');
    return (
      <>
        <div>{this.state.num}</div>
        <button onClick={this.handleOnClick}>加1</button>
      </>
    )
  }
}

image
最终this.state是3,且每次setState中拿到的state(注意不是this.state)都是我们预期修改后的,而且根据调用顺序来看,虽然确实执行了多次setState,但最终对于this.state的修改只有一次,且render只执行了一次,这种情况下react依旧做了批量合并处理。

贰 ❀ 叁 什么情况下setState是同步?

其实要回到这个问题,我们只需要知道什么情况下setState是异步,那么反过来的情况自然就都是同步了。一般来说,react在事件处理函数内部的 setState 都是异步的,比如合成事件onClick,onBlur,其次react提供的生命周期钩子函数中也是异步。

那么是不是说只要setState不在合成事件内调用,我们就能实现同步更新了呢?来看个例子:

class Echo extends React.Component {
  state = {
    num:1
  }

  componentDidUpdate() {
    console.log(this.state.num);//2 3 4
  }

  handleOnClick = () => {
    setTimeout(()=>{
      this.setState({num:this.state.num+1});
      this.setState({num:this.state.num+1});
      this.setState({num:this.state.num+1});
      console.log(this.state.num);//4
    })
  }

  render() {
    console.log('我在render了');// 执行3次
    return (
      <>
        <button onClick={this.handleOnClick}>click me</button>
      </>
    )
  }
}

image

事实上,超出了react能力范畴之外的上下文,比如原生事件,定时器回调等等,在这里面进行setState操作都会同步更新state。比如在上述例子中,我们实现了在setState后获取到同步更新的this.state,但遗憾的是,react此时并不能做到批量合并操作,导致render执行了三次。

原文链接:https://blog.csdn.net/echolunzi/article/details/125560894

原文地址:http://www.cnblogs.com/coderz1/p/16838096.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性