深入React技术栈之setState详解
抛出问题
class Example extends Component {
contructor () {
super()
this.state = {
value: 0,
index: 0
}
}
componentDidMount () {
this.setState({value: this.state.value + 1})
console.log(this.state.value) // 第一次输出
this.setState({value: this.state.value + 1})
console.log(this.state.value) // 第二次输出
setTimeout(() => {
this.setState({value: this.state.value + 1})
console.log(this.state.value) // 第三次输出
this.setState({value: this.state.value + 1})
console.log(this.state.value) // 第四次输出
}, 0);
this.refs.button.addEventListener('click', this.click)
}
click = () => {
this.setState({value: this.state.index + 1})
this.setState({value: this.state.index + 1})
}
render () {
return (
<div><span>value: {this.state.value}index: {this.props.index}</span>
<button ref="button" onClick={this.click}>点击</button>
</div>
)
}
}
- 这四次输出,按常理来说分别是: 1,2,3,4。但是,实际输出为: 0, 0, 2, 3
setState的注意点
setState不会立刻改变React组件中state的值(即setState是异步更新)
- setState通过一个队列机制实现state更新;
- 当执行setState时,会将需要更新的state合并后放入状态队列,而不会立即更新,队列可以高效的批量更新state;
- 通过this.state直接修改的值,state不会放入状态队列,当下次调用setState并对状态队列进行合并时,会忽略之前直接被修改的state.
setState通过引发一次组件的更新过程来引发重新绘制
- 此处重绘指的就是引起React的更新生命周期函数4个函数:
- shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新)
- componentWillUpdate(被调用时this.state没有更新)
- render(被调用时this.state得到更新)
- componentDidUpdate
- 多个相邻的state的修改可能会合并到一起一次执行
this.setState({name: 'Pororo'})
this.setState({age: 20})
- 等同于
this.setState({name: 'Pororo',age: 20})
- 上面两块代码的效果是一样的。如果每次调用都引发一次生命周期更新,那性能就会消耗很大了。所以,React会将多个this.setState产生的修改放进一个队列里,等差不多的时候就会引发一次生命周期更新。
问题分析
- 对于前两次setState:
this.setState({value: this.state.val + 1});
console.log(this.state.value); // 第一次输出
this.setState({value: this.state.val + 1});
console.log(this.state.value); // 第二次输出
- 由于setState不会立即改变React组件中state的值,所以两次setState中this.state.value都是同一个值0,故而,这两次输出都是0。因而value只被加1。
- 既然这样,那么是不是可以直接操作this.state呢?比如:
this.state.value=this.state.value+1;
- 这样的确可以修改this.state.value的状态但是却不可以引发重复渲染。
- 所以,就必须通过React设定的setState函数去改变this.state,从而引发重新渲染。
- setTimeout里面的两次setState:
setTimeout(() => {
this.setState({value: this.state.value + 1})
console.log(this.state.value) // 第三次输出
this.setState({value: this.state.value + 1})
console.log(this.state.value) // 第四次输出
}, 0);
- 这两次this.state的值同步更新了;
- 同步更新:是由React引发的事件处理(比如:onClick引发的事件处理),调用setState会异步更新this.state;
- 异步更新:除此之外的setState调用会同步执行this.setState。 “除此之外”指的是:绕过React通过addEventListener直接添加的事件处理函数和setTimeout/setInterval产生的异步调用。
- this.setState更新机制图解:
- 每次setState产生新的state会依次被存入一个队列,然后会根据isBathingUpdates变量判断是直接更新this.state还是放进dirtyComponent里回头再说。
- isBatchingUpdates默认是false,也就表示setState会同步更新this.state。
- 但是,当React在调用事件处理函数之前就会调用batchedUpdates,这个函数会把isBatchingUpdates修改为true,造成的后果就是由React控制的事件处理过程setState不会同步更新this.state。
同步更新(函数式setState)
- 如果this.setState的参数不是一个对象而是一个函数时,这个函数会接收到两个参数,第一个是当前的state值,第二个是当前的props,这个函数应该返回一个对象,这个对象代表想要对this.state的更改;
- 换句话说,之前你想给this.setState传递什么对象参数,在这种函数里就返回什么对象。不过,计算这个对象的方法有些改变,不再依赖于this.state,而是依赖于输入参数state。
function increment(state, props) {
return {count: state.count + 1};
}
function incrementMultiple() {
this.setState(increment);
this.setState(increment);
this.setState(increment);
}
- 假如当前this.state.count的值是0,第一次调用this.setState(increment),传给increment的state参数是0,第二调用时,state参数是1,第三次调用是,参数是2,最终incrementMultiple让this.state.count变成了3。
- 对于多次调用函数式setState的情况,React会保证调用每次increment时,state都已经合并了之前的状态修改结果。
要注意的是,在increment函数被调用时,this.state并没有被改变,依然,要等到render函数被重新执行时(或者shouldComponentUpdate函数返回false之后)才被改变。
同步异步setState的用法混合
function incrementMultiple() {
this.setState(increment);
this.setState(increment);
this.setState({count: this.state.count + 1});
this.setState(increment);
}
- 在几个函数式setState调用中插入一个传统式setState调用,最后得到的结果是让this.state.count增加了2,而不是增加4。
- 这是因为React会依次合并所有setState产生的效果,虽然前两个函数式setState调用产生的效果是count加2,但是中间出现一个传统式setState调用,一下子强行把积攒的效果清空,用count加1取代。
- 所以,传统式setState与函数式setState一定不要混用。
总结自:掘金(不洗碗工作室)
原文地址:https://segmentfault.com/a/1190000014990454
深入React技术栈之setState详解的更多相关文章
- ELK技术栈之-Logstash详解
ELK技术栈之-Logstash详解 前言 在第九章节中,我们已经安装好Logstash组件了,并且启动实例测试它的数据输入和输出,但是用的是最简单的控制台标准输入和标准输出,那这节我们就来深入的 ...
- 应用编排服务之ELK技术栈示例模板详解
日志对互联网应用的运维尤为重要,它可以帮助我们了解服务的运行状态.了解数据流量来源甚至可以帮助我们分析用户的行为等.当进行故障排查时,我们希望能够快速的进行日志查询和过滤,以便精准的定位并解决问题. ...
- React源码 commit阶段详解
转: React源码 commit阶段详解 点击进入React源码调试仓库. 当render阶段完成后,意味着在内存中构建的workInProgress树所有更新工作已经完成,这包括树中fiber节点 ...
- 重谈react优势——react技术栈回顾
react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ...
- Docker 基础技术之 Linux cgroups 详解
PS:欢迎大家关注我的公众号:aCloudDeveloper,专注技术分享,努力打造干货分享平台,二维码在文末可以扫,谢谢大家. 推荐大家到公众号阅读,那里阅读体验更好,也沉淀了很多篇干货. 前面两篇 ...
- C/C++堆、栈及静态数据区详解
转自:https://www.cnblogs.com/hanyonglu/archive/2011/04/12/2014212.html 做略微修改 C/C++堆.栈及静态数据区详解 本文介绍C ...
- php缓存技术——memcache常用函数详解
php缓存技术——memcache常用函数详解 2016-04-07 aileen PHP编程 Memcache函数库是在PECL(PHP Extension Community Library)中, ...
- react技术栈实践(2)
本文来自网易云社区 作者:汪洋 这时候还没完,又有两个问题引出来了. 按照上面的配置,第三方库 antd 竟然也被编译了,导致样式失败. react中,一旦包裹了子组件,子组件没办法直接使用 styl ...
- react第五单元(事件系统-原生事件-react中的合成事件-详解事件的冒泡和捕获机制)
第五单元(事件系统-原生事件-react中的合成事件-详解事件的冒泡和捕获机制) 课程目标 深入理解和掌握事件的冒泡及捕获机制 理解react中的合成事件的本质 在react组件中合理的使用原生事件 ...
随机推荐
- bzoj 2016: [Usaco2010]Chocolate Eating【二分+贪心】
二分答案,贪心判断,洛谷上要开long long #include<iostream> #include<cstdio> using namespace std; const ...
- [POI2008]BLO-Blockade 【无向图tarjan/鸽点】By cellur925
题目传送门 lyd无向图tarjan的例题. 一句话题意(不得不佩服lyd老师的高度概括能力):在一张无向连通图上,求出每个点被破坏(去掉与这个点相关的所有边,不去掉这个点)后,无向图中使i,j不连通 ...
- python爬虫爬取腾讯招聘信息 (静态爬虫)
环境: windows7,python3.4 代码:(亲测可正常执行) import requests from bs4 import BeautifulSoup from math import c ...
- Unix\Linux | 总结笔记 | vi编辑器
0 目录 新建文件夹 常用的命令 三种模式及切换 vi编辑器的启动选项及后续相关操作 其他--替换文本 其他--写入另一个文件 1 新建文件夹 vi [目录名/文件 ...
- 【数据结构(C语言版)系列二】 栈
栈和队列是两种重要的线性结构.从数据结构角度看,栈和队列也是线性表,但它们是操作受限的线性表,因此,可称为限定性的数据结构.但从数据类型角度看,它们是和线性表大不相同的两类重要的抽象数据类型. 栈的定 ...
- SolrCloud索引富文本数据
solrconfig配置文件: schema配置文件: 执行目录: /opt/solr-5.5.4/server/scripts/cloud-scripts -- 下载配置文件 ./zkcli.sh ...
- C#内存映射文件学习[转]
原文链接 内存映射文件是由一个文件到进程地址空间的映射. C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen).内存映射文件与虚拟内存有些类似, ...
- Linux oraenv Tips
Linux for the Oracle DBA -Customizing the Oracle User's Environment There are many ways to customize ...
- 基于CentOS6.5或Ubuntu14.04下Suricata里搭配安装 ELK (elasticsearch, logstash, kibana)(图文详解)
前期博客 基于CentOS6.5下Suricata(一款高性能的网络IDS.IPS和网络安全监控引擎)的搭建(图文详解)(博主推荐) 基于Ubuntu14.04下Suricata(一款高性能的网络ID ...
- form表单ajaxSubmit提交并验证
html: <form class="register-form" action="{:U('')}" method="post"&g ...