当前位置: 首页 > news >正文

15. setState的更新是异步的吗

setState的更新是异步的?
- 最终打印结果是Hello World;
- 可见setState是异步的操作,我们并不能在执行完setState之后里面拿到最新的state的结果
- 异步执行结果如下图
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1.3.3. 为什么setState设计为异步呢?
    • setState设计为异步其实之前在GitHub上也有很多的讨论;
    • React核心成员(Redux的作者)Dan Abramov也有对应的回复,有兴趣的可以参考下一下链接
    • 官网地址:https://github.com/facebook/react/issues/11527#issuecomment-360199710
  • 1.3.4. 我对其回答做一个简单的总结:
      1. setState设计为异步,可以显著的提升性能;
      • 1.1. 如果每次调用setState都进行一次更新,那么以为意味着render函数会被频繁调用,界面重选渲染那,这样效率很低的;
      • 1.2. 最好的办法应该是获取到多个更新,之后进行批量更新;
      1. 如果同步更新了state, 但是还没有执行render函数,那么state和props不能保持同步;
      • 2.1. state和props不能保持一致性,会在开发中产生很多的问题;
      1. setState批量更新机制:
      • 3.1. React18之前是同步的,React18之后都是异步的
      • 3.2. 这里调用了setState三次render函数就会执行三次进行三次diff算法dom会更新三次dom更新的话会引起回流和重绘
      • 3.3. 为什么用调用setState三次?
        • 理想情况下可以直接调用counter + 3
        • 但是在真实开发中会出现componentDidMount里面同时发送网络请求然后差不多时间段返回数据让setState执行多次进行赋值,但是更新state只要更新一次就可以了,更新只要更新一次就可以了
      • 3.4 所以最好的办法:获取到多个更新,之后进行批量更新(一起做一个合并,合并完值之后调用一次render函数就可以,对DOM也是进行一次diff算法,对DOM进行一次更新)
      • 3.5 React18之前有一些地方是不会做批量处理的,例如:promise的then回调不会做批处理,React18之后所有东西都是批量处理
      • 3.6. 关键代码代码如下:
          this.setState({counter: this.state.counter + 1  // 0 + 1})this.setState({counter: this.state.counter + 1 // 0 + 1})this.setState({counter: this.state.counter + 1 // 0 + 1})
        
      1. setState异步更新的原理:
      • 原理:当执setState更新值操作的时候,会做一个批量处理,把要更新的东西做一个异步操作,先不更新。搞一个队列,会把更新的操作加入到队列里面,等到需要真正更新的时候,取出每次需要更新具体的内容,进行依次合并,队列是有顺序的,队列的特点就是先进先出;
      • 内部源码:内部做了一个do{} while循环,用do{} while循环把队列里面所有东西都给取出来,取出来之后挨个合并合并完之后在执行一次render函数
      1. 上面的代码就会被合并,只会执行一次,真的会被合并吗?
      • 这样来的写的话 this.state.counter + 1,this.state.counter取到的是一个值为0, 所以是: 0 + 1,依然是有合并的,在构建对象的时候,就已经把counter的值取出来了,所以是0+1为1,所以相当于把下面的代码三步做了一样的操作0+1,这里还没涉及设置它
      • 有点类似于obj1的操作 const obj1 = { counter: 0 + 1 }
      • 但是结果是为1的,每次设置值,都是0+1这个值,这个东西同时依然有合并,可以验证它的合并过程,值为1, render函数只执行了一次
      • 如下图:
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 关键代码如下:
       export class App extends Component {constructor() {super()this.state ={message: 'hello world',counter: 0}}increment () {console.log('-----------')this.setState({counter: this.state.counter + 1  // 0 + 1})this.setState({counter: this.state.counter + 1 // 0 + 1})this.setState({counter: this.state.counter + 1 // 0 + 1})}render() {const { message, counter } = this.stateconsole.log('render被执行')return (<div><h2>message: {message}</h2><button onClick = {e => this.changeText()}>修改文本</button><h2>当前计数:{ counter }</h2><button onClick= { e => this.increment()}>+1</button><Hello message={message} /></div>)}}
      
      1. 其实上面这段代码刚好可以验证是异步的,如果是同步的话,第一个setState更新完之后结果应该是1,第二个setState更新完之后结果应该是2, 第三个setState更新完之后结果应该是3
        但是它没有,结果依旧是1,所以间接证明了它就是异步的,render函数执行一次
      1. 现在合并的值依然是之前的值,如果想要真正的被合并,传入一个回调函数
      • 运行结果如下图:
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 关键代码如下:
        // 现在合并的值依然是之前的值,如果想要真正的被合并,传入一个回调函数// 这里主要讨论了两个问题:// 一:就是在一个点击函数里面执行了三次setState, render函数也只会被执行一次// 二:三次里面刚好出现了递增的情况, 需要传入回调函数,在回调函数里面使用传入进来的state, 因为传入进来的state已经是合并完之后另外一个state的值(更新后的值)this.setState((state) =>{// 使用传递进来state// 这个state里面的数据更新 这个本质上是放到一个队列,队列里面放了一个个函数,到时候取出来的时候会回调这个函数,回调这个函数的时候会传入一个state,回调这个函数的时候会把最新的state传进来// 经过函数的调用的话就会做一个+1的操作,紧接着取出来第二个函数的时候,又要传入一个state,原来的state就已经+1了,所以传进来的state就是另外一个state(更新计算后的state值), 拿到最新的state进行回调// 这里拿到的this.state的值是不是正确的?// 回调这个函数的时候准备做更新了, 不是加入到队列,已经从队列里面取出来了准备开始执行了 ,这个地方this.state的值有没有更新取决于上次更新有没有把上一次this.state里的值有没有改掉还是一次性改掉// 取到的值依然是0,那就是依然没有更改,做一次性更改,等到所有东西都合并到一起之后再做一次性更新// console.log(this.state.counter)return {counter: state.counter + 1}})this.setState((state) =>{return {counter: state.counter + 1}})this.setState((state) =>{return {counter: state.counter + 1}})
      
      1. 3./4./5./6./7.主要讨论了两个问题:
        1. 问题一:就是在一个点击函数里面执行了三次setState, render函数也只会被执行一次
        1. 问题一:调用三次setState里面刚好出现了递增的情况, 需要传入回调函数,在回调函数里面使用传入进来的state, 因为传入进来的state已经是合并完之后另外一个state的值(更新后的值)
  • 1.4. 如何获取异步的结果
      1. 那么如何可以获取更新后的值呢?
      1. 方式一: setState的回调
      • 2.1. setState接受两个参数,第二个参数是一个回调函数,这个回调函数会在更新后执行;
      • 2.2. 格式如下:setState(partialState, callback)
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      1. 方式二: 也可以在生命周期函数:
        componentDidUpdate(prevProps, prevState, snapshot) {  console.log(this.state.message);}
      
  • 1.5. setState一定是异步吗?(React18之前)
      1. 验证一:setTimeout中的更新
        setTimeout(() => {// 在React18之前,setTimeout中setState操作,是同步操作// 在React18之后,setTimeout中setState是异步操作(批处理)this.setState({ message: '你好啊,李银河' })console.log(this.state.message)  // React18之前`你好啊,李银河`   React18之后`hello World`}, 0)
      1. 验证二:原生DOM事件
        componentDidMount() {const btnEl = document.getElementById('btn');btnEl.addEventListener('click', () => {this.setState({ message: '你好啊,李银河' }) // React18之前`你好啊,李银河`})}
      
        1. 其实分层两种情况:
          1. 在组件生命周期或React合成事件(React事件回调), setState是异步;
          1. 在setTimeou或者原生dom事件中,setState是同步;
  • 1.6. setState一定是异步吗?(React18之后)
      1. 在React18之后,默认所有的操作都被放到了批处理中(异步处理)
      • 官网截图:
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      1. 如果希望代码可以同步拿到,则需要执行特殊的flushSync操作;
     // 导入flushSyncimport { flushSync } from 'react-dom'// 使用flushSync,传入一个回调函数flushSync(() => {this.setState({ message: '你好啊,李银河' })// 这里的代码依然是批处理,这个回调里面的函数里的代码都是当成一次更新console.log('flushSync====',this.state.message) // hello world})console.log(this.state.message) // `你好啊,李银河`
    
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
http://www.dtcms.com/a/552839.html

相关文章:

  • Qwen2.5-VL开源,斩获多榜单冠军!
  • Prometheus和Grafana简介
  • 基于深度学习的医疗器械分类编码映射系统:设计篇
  • Rust开发之自定义错误类型(实现Error trait)
  • 【Java Web学习 | 第三篇】CSS(2) - 元素显示模式
  • 10月31日
  • Mybatis-Plus实现MySQL分表
  • 兵团住房和城乡建设局网站网站设计标杆企业
  • 快充新标杆:AVS 协议如何重塑手机充电体验
  • LIUNX 与手机安卓的文件互传 的常用方法
  • 第一届数证杯做题笔记(流量分析和手机取证)
  • 【IO多路转接】深入解析 poll:从接口到服务器实现
  • 【Spring Boot】Spring Boot解决循环依赖
  • 网站开发发展趋势2018网上建立网站赚钱
  • SuperMap Hi-Fi 3D SDK for Unreal 使用蓝图接口加载多源数据
  • 【Java】如何使用jdbc连接并操作MySQL,一文读
  • SSM宠物寄养系统ih041gj7(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 网站广告素材php网站免费模板
  • 还在用JDK8?JDK8升级JDK11:一次价值千万的升级指南
  • 深圳网站建设模板乐云seo与设计行业相关的网站
  • 缓存异常:缓存穿透、缓存击穿、缓存雪崩
  • 【计算机网络】IO复用方法(二)——Select
  • 【Java EE进阶 --- SpringBoot】统一功能处理(拦截器)
  • 主流数据分析工具全景对比:Excel / Python / R / Power BI / Tableau / Qlik / Snowflake
  • 从被动防御到主动管控:雷池SafeLine的远程安全运营之道
  • 人体静电消除器安全设计 蒙冬智能
  • 我想要个网站深圳最新招聘
  • Hybrid OCR-LLM框架用于在大量复杂密集企业级文档信息提取
  • 仙居做网站在哪里做项目网格化管理方案
  • ubuntu部署whisper+speaker_large+qwen【一】