首页 > 其他分享 >react中的setState是同步还是异步?react为什么要将其设计成异步?

react中的setState是同步还是异步?react为什么要将其设计成异步?

时间:2022-10-29 09:55:53浏览次数:88  
标签:异步 console log react state num setState

壹 ❀ 引

了解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

标签:异步,console,log,react,state,num,setState
From: https://www.cnblogs.com/coderz1/p/16838096.html

相关文章

  • 前端react面试题总结
    为什么调用setState而不是直接改变state?解答如果您尝试直接改变组件的状态,React将无法得知它需要重新渲染组件。通过使用setState()方法,React可以更新组件的UI。另......
  • 面试官让你说说react状态管理?
    开发者普遍认为状态是组件的一部分,但是同时却又在剥离状态上不停的造轮子,这不是很矛盾么?对于一个最简单的文本组件而言functionText(){const[text,setText]=......
  • 面试官:React怎么做性能优化
    前言最近一直在学习关于React方面的知识,并有幸正好得到一个机会将其用在了实际的项目中。所以我打算以博客的形式,将我在学习和开发(React)过程中遇到的问题记录下来。这两......
  • 异步发送mq消息sdk源码分析
    1.异步发送API接口publicvoidsend(finalMessagemessage,finalAsyncSendCallbackcallback)2.实现类分析publicvoidsend(finalMessagemessage,finalAsyncS......
  • 每日一题之Vue的异步更新实现原理是怎样的?
    最近面试总是会被问到这么一个问题:在使用vue的时候,将for循环中声明的变量i从1增加到100,然后将i展示到页面上,页面上的i是从1跳到100,还是会怎样?答案当然是只会显示100,并不会......
  • 问:React的useState和setState到底是同步还是异步呢?
    先来思考一个老生常谈的问题,setState是同步还是异步?再深入思考一下,useState是同步还是异步呢?我们来写几个demo试验一下。先看useState同步和异步情况下,连续执行两......
  • 问:你是如何进行react状态管理方案选择的?
    前言:最近接触到一种新的(对我个人而言)状态管理方式,它没有采用现有的开源库,如redux、mobx等,也没有使用传统的useContext,而是用useState+useEffect写了一个发布订阅者模式进......
  • 腾讯前端经典react面试题汇总
    概述一下React中的事件处理逻辑。为了解决跨浏览器兼容性问题,React会将浏览器原生事件(BrowserNativeEvent)封装为合成事件(SyntheticEvent)并传入设置的事件处理程序......
  • React hooks useReducer
    useReducer函数与redux中reducer函数如出一辙。在hooks函数中就是useState函数的替代方案。它接收一个形如(state,action)=>newState的reducer,并返回当前的state以......
  • React hooks useContext
    useContext():共享状态钩子该钩子的作用是,在组件之间共享状态。关于Context这里不再赘述,其作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数......