博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React-setState杂记
阅读量:6612 次
发布时间:2019-06-24

本文共 3330 字,大约阅读时间需要 11 分钟。

前言

在看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为什么要异步更新,它是怎么做的?

深入源码你会发现:(引用程墨老师的)

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

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

image

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

image

中心意思大概就是:

同步更新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控制的方法, 即为同步更新, 如下(引用程墨老师的)
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 (    
{this.state.count}
)}

有的人也会想能不能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不能放弃。

转载地址:http://oeaso.baihongyu.com/

你可能感兴趣的文章
css3实现可以计算的自适应布局——calc()
查看>>
理解JavaScript私有作用域
查看>>
Codeforces 626F Group Projects(滚动数组+差分dp)
查看>>
Docker实战之创建一个tomcat容器
查看>>
【Postgresql】use
查看>>
BZOJ 1012: [JSOI2008]最大数maxnumber【线段树单点更新求最值,单调队列,多解】
查看>>
php 把一个数组分成有n个元素的二维数组的算法
查看>>
利用模板导出文件(二)之jacob利用word模板导出word文件(Java2word)
查看>>
OAuth 2.0
查看>>
SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域
查看>>
Drupal 7模板(主题钩子)的建议
查看>>
nginx配置文件中location说明
查看>>
asp.net mvc3开发系统点滴之二
查看>>
连载-第1章绪论 1.1嵌入式系统概述
查看>>
UltraVNC
查看>>
详解synchronized
查看>>
Spring Cloud第二篇 创建一个Eureka Server
查看>>
vue学习记录 v-on事件绑定
查看>>
webpack基本应用
查看>>
初探数据双向绑定
查看>>