React-setState杂记

2019-02-12 admin

前言

在看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。

然后我在网上引用了这张图(侵删) 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控制的方法, 即为同步更新, 如下(引用程墨老师的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不能放弃。

[转载]原文链接:https://segmentfault.com/a/1190000018126115

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-53576.html

文章标题:React-setState杂记

相关文章
webpack入门+react环境配置
本文介绍了react.js使用webpack搭配环境的入门教程,分享给大家,也给自己做个笔记 如果你想直接上手开发,而跳过这些搭配环境的繁琐过程,推荐你使用官方的create-react-app命令 npm install -g creat...
2017-03-20
#react-native# Error: Command failed: gradlew.bat installDebug
这篇文章主要解决react-native中遇到的bug。 编译并运行 React Native 应用之前我们可以使用下面的方法清理gradlew 和之前的构建文件。 Error: Command failed: gradlew.bat in...
2019-02-28
React-Navigation 导航综合应用
本文由我们团队的 杨俊宁 组内分享后总结。 移动应用中最常见的导航风格可能是基于 tab 的导航。 可以在屏幕底部或顶部的标题栏下方(甚至取代标题栏)。 createStackNavigator 创建的 stack navigator...
2018-06-27
React水印组件,支持图片水印,文字水印
React水印组件,支持图片水印,文字水印。 安装 npm i --save react-watermark-module 用法 import ReactWatermark from &#x27;react-watermark-modul...
2018-01-09
跟着例子一步步学习redux+react-redux
前言 本文不会拿redux、react-redux等一些react的名词去讲解,然后把各自用法举例说明,这样其实对一些react新手或者不太熟悉redux模式的开发人员不够友好,他们并不知道这样使用的原因。本文通过一个简单的例子展开,一点点...
2018-01-26
使用 React 和 Django REST Framework 构建你的网站
此专栏我会不定期分享一些 Django 最前沿的文章,内容偏重技巧、经验的归纳总结,来源暂时有: Medium Twitter 知名博主 如果大家感兴趣,请一定点个关注,给我一些动力,毕竟翻译整理是需要时间的,谢谢大家 – 原文地址:...
2018-02-08
React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案
本文是学习了2018年新鲜出炉的React Hooks提案之后,针对异步请求数据写的一个案例。注意,本文假设了: 1.你已经初步了解hooks的含义了,如果不了解还请移步官方文档。(其实有过翻译的想法,不过印记中文一直在翻译,就是比较慢啦...
2018-12-01
create-react-app配置多页应用
背景:最近接手一个混合app项目,但是其中的页面都是用jquery写的,有点乱,所以决定用react重构一下,因为涉及到很多页面,所以create-react-app只能自己配置成多页的了。弄这个也弄了挺久的,主要是对webpack的各个...
2018-01-10
react中fetch之cors跨域请求的实现方法
项目中使用了react,当中需要使用fetch来代替ajax。 由于react的create_react_app工具很方便,基本上开箱即用,经过创建项目,输入npm start命令后,便自动监听一个3000的端口,到此前端部分就绪。 具体参...
2018-03-15
超级给力的JavaScript的React框架入门教程
React 是 Facebook 里一群牛 X 的码农折腾出的牛X的框架。 实现了一个虚拟 DOM,用 DOM 的方式将需要的组件秒加,用不着的秒删。React 扮演着 MVC 结构中 V 的角色, 不过你要是 Flux 搭配使用, 你就有...
2017-03-27
回到顶部