深入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组件中合理的使用原生事件 ...
随机推荐
- 洛谷 P3357 最长k可重线段集问题【最大流】
pre:http://www.cnblogs.com/lokiii/p/8435499.html 和最长k可重区间集问题差不多,也就是价值的计算方法不一样,但是注意这里可能会有x0==x1的情况也就是 ...
- [转]POJ WA/RE指南
"POJ上头的题都是数学题",也不知道是那个家伙胡诌的--但是POJ的要求就是算法通过了也不让你AC.下面本人就这560题的经验,浅谈一下WA/RE了怎么办. 以下内容是扯淡-- ...
- SSH的安装及使用
ssh(secure shell--加密远程登录管理服务器) ,是一种安全的传输协议,主要用于给远程登录会话的数据进行加密,保证数据传输的安全.Ubuntu客户端可以通过SSH访问远程服务器.SSH的 ...
- 贪心 UVALive 6832 Bit String Reordering
题目传送门 /* 贪心:按照0或1开头,若不符合,选择后面最近的进行交换.然后选取最少的交换次数 */ #include <cstdio> #include <algorithm&g ...
- java数组实现买彩票(二个一维数组的比较思想)
/** 设计一个程序,模拟从彩球池里随机抽取5个彩球(彩球池里一共有11个彩球,编号为1~11), 要求在控制台打印出这5个被取出来的彩球的编号(注意编号不能重复). 思路: 1.创建一个int类型的 ...
- VS打包后生成快捷方式:目标指向错误、Icon图标分辨率有误问题解决方案
1.目标指向错误: 在安装***.msi文件后,对快捷方式-->右键-->属性: 发现目标并非指exe文件. 于是我新建了一个快捷方式,将目标-->指向exe文件,位置Ctrl+v. ...
- c#中stringbuilder的方法总结
String 对象是不可改变的.每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间.在需要对字符串执行重复修改的情况下,与创建新 ...
- 【原】无脑操作:Eclipse + Maven + jFinal + MariaDB 环境搭建
一.开发环境 1.windows 7 企业版 2.Eclipse IDE for Enterprise Java Developers Version: 2019-03 (4.11.0) 3.JDK ...
- AJPFX简述可变参数概述和使用
A:可变参数概述 定义方法的时候不知道该定义多少个参数 B:格式 修饰符 返回值类型 方法名(数据类型… 变量名){} C:注意事项: 这里的变量其实是一个数组 如果一个方法有可变参数,并且有多个参数 ...
- Java数据类型和MySql数据类型对应一览 [转]
类型名称 显示长度 数据库类型 JAVA类型 JDBC类型索引(int) 描述 VARCHAR L+N VARCHAR java.lang.String 12 CHAR N ...