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

React-setState杂记

前言

在看React的官方文档的时候, 发现了这么一句话,State Updates May Be Asynchronous,于是查询了一波资料, 最后归纳成以下3个问题

  • setState为什么要异步更新,它是怎么做的?
  • setState什么时候会异步更新, 什么时候会同步更新?
  • 既然setState需要异步更新, 为什么不让用户可以同步读到state的新值,但更新仍然是异步?

常见场景下的异步更新

以下是官方文档的一个例子, 调用了3次incrementCount方法, 期望this.state.count的值是3, 但最后却是1

incrementCount() {
  this.setState({count: this.state.count + 1});
}

handleSomething() {
  // Let's say `this.state.count` starts at 0.
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();
  // When React re-renders the component, `this.state.count` will be 1, but you expected 3.

  // This is because `incrementCount()` function above reads from `this.state.count`,
  // but React doesn't update `this.state.count` until the component is re-rendered.
  // So `incrementCount()` ends up reading `this.state.count` as 0 every time, and sets it to 1.

  // The fix is described below!
}
复制代码

那么就可以引出第一个问题

setState为什么要异步更新,它是怎么做的?

深入源码你会发现:(引用程墨老师的setState何时同步更新状态)

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中回头再说, 而 isBatchingUpdates 默认是 false,也就表示 setState 会同步更新 this.state, 但是,有一个函数 batchedUpdates,这个函数会把 isBatchingUpdates 修改为 true, 而当 React 在调用事件处理函数之前就会调用这个 batchedUpdates,造成的后果,就是由 React 控制的事件处理过程 setState 不会同步更新 this.state。

然后我在网上引用了这张图(侵删)

从结论和图都可以得出, setState是一个batching的过程, React官方认为, setState会导致re-rederning, 而re-rederning的代价是昂贵的, 所以他们会尽可能的把多次操作合并成一次提交。以下这段话是Dan在Issue中的回答:

中心意思大概就是:
同步更新setState并re-rendering的话在大部分情况下是无益的, 采用batching会有利于性能的提升, 例如当我们在浏览器插入一个点击事件时,父子组件都调用了setState,在batching的情况下, 我们就不需要re-render两次孩子组件,并且在退出事件之前re-render一次即可。

那么如果我们想立即读取state的值, 其实还有一个方法, 如下代码:
因为当传入的是一个函数时,state读取的是pending队列中state的值

incrementCount() {
  this.setState((state) => {
    // Important: read `state` instead of `this.state` when updating.
    return {count: state.count + 1}
  });
}

handleSomething() {
  // Let's say `this.state.count` starts at 0.
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();

  // If you read `this.state.count` now, it would still be 0.
  // But when React re-renders the component, it will be 3.
}
复制代码

当然, 仔细看React文档的话, 可以发现, State Updates May Be Asynchronou里面有一个may的字眼,也就是可能是异步更新, 因而引出第二个问题

setState什么时候会异步更新, 什么时候会同步更新?

其实从第一个问题中我们就知道,React是根据isBatchingUpdates来合并更新的, 那么当调用setState的方法或者函数不是由React控制的话, setState自然就是同步更新了。

简单的举下例子:

  1. 如componentDidMount等生命周期以及React的事件即为异步更新,这里不显示具体代码。
  2. 如自定义的浏览器事件,setTimeout,setInterval等脱离React控制的方法, 即为同步更新, 如下(引用程墨老师的setState何时同步更新状态)
componentDidMount() {
  document.querySelector('#btn-raw').addEventListener('click', this.onClick);
}
onClick() {
  this.setState({count: this.state.count + 1});
  console.log('# this.state', this.state);
}
// ......
render() {
  console.log('#enter render');
  return (
    <div>
      <div>{this.state.count}
        <button id="btn-raw">Increment Raw</button>
      </div>
    </div>
  )
}
复制代码

有的人也会想能不能React依然合并更新, 但用户可以同步读取this.state的值, 这个问题在React的一个Issue上有提到, 也是我们的第三个问题

既然setState需要异步更新, 为什么不让用户可以同步读到state的新值,但更新仍然是异步?

这个问题可以直接在Dan的回答中得到:

This is because, in the model you proposed, this.state would be flushed immediately but this.props wouldn’t. And we can’t immediately flush this.props without re-rendering the parent, which means we would have to give up on batching (which, depending on the case, can degrade the performance very significantly).

大概意思就是说:

如果在应用中,this.state的值是同步,但是this.props却不是同步的。因为props只有当re-rendering父组件后才传给子组件,那么如果要props变成同步的, 就需要放弃batching。 但是batching不能放弃。

相关文章:

  • Android开发学习笔记:浅谈GridView
  • 我从来不理解JavaScript闭包,直到有人这样向我解释它...
  • ExtJs自学教程(1):一切从API開始
  • Create React App 使用
  • 演练5-5:Contoso大学校园管理系统5
  • [USACO12DEC]逃跑的BarnRunning Away From…
  • SpiderData 2019年2月13日 DApp数据排行榜
  • css按钮渐变色
  • 如何胜任知名企业的商业数据分析师?
  • 网站优化技术
  • AI和ML自动化测试是骗人的营销手段吗?
  • C# U盘扫描
  • 工欲善其事必先利其器之8266
  • 2019年面对全新的DDoS功击企业需做好哪些防护措施?
  • GCD1:构建Block Objects
  • JavaScript 基础知识 - 入门篇(一)
  • javascript从右向左截取指定位数字符的3种方法
  • JavaScript中的对象个人分享
  • Nodejs和JavaWeb协助开发
  • Redis学习笔记 - pipline(流水线、管道)
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 基于组件的设计工作流与界面抽象
  • 码农张的Bug人生 - 见面之礼
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 微服务核心架构梳理
  • 微信小程序开发问题汇总
  • 协程
  • 原生 js 实现移动端 Touch 滑动反弹
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • "无招胜有招"nbsp;史上最全的互…
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • (Git) gitignore基础使用
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (五)MySQL的备份及恢复
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .“空心村”成因分析及解决对策122344
  • .describe() python_Python-Win32com-Excel
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET 使用 XPath 来读写 XML 文件
  • :如何用SQL脚本保存存储过程返回的结果集
  • @javax.ws.rs Webservice注解
  • @requestBody写与不写的情况
  • @RequestParam,@RequestBody和@PathVariable 区别
  • [ C++ ] STL_stack(栈)queue(队列)使用及其重要接口模拟实现
  • [].shift.call( arguments ) 和 [].slice.call( arguments )
  • [CSAWQual 2019]Web_Unagi ---不会编程的崽
  • [Linux] LVS+Keepalived高可用集群部署
  • [linux]linux命令学习-netstat
  • [linux运维] 利用zabbix监控linux高危命令并发送告警(基于Zabbix 6)
  • [Mac软件]Goldie App v2.2 Mac黄金比例设计工具
  • [NOI2012]迷失游乐园