理解setState
近来在学习react源码, 最初是直接从入口一行一行的看, 结果跟着调用的函数跳转来跳去头都晕了. 后来决定带着一个目的去看源码, 每次看只研究一个东西. 一开始最想了解的就是充满魔性的setState. 本文是我对setState的一些理解, 不当之处欢迎留言指正.
setState的魔性
看一下下边几个例子的输出情况.
例1 合成事件中的setState
import React from 'react';
export default class SetState extends React.Component {
constructor(props) {
super(props);
}
state = {
count: 0
}
click = () => {
this.setState({
count: this.state.count + 1,
})
console.log('count1', this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log('count2', this.state.count);
}
render() {
return (
<div onClick={this.click}>
count的值{this.state.count}
</div>
)
}
}
// 打印:
// count1 0
// count2 0
例2 生命周期函数中的setState
import React from 'react';
export default class SetState extends React.Component {
constructor(props) {
super(props);
}
state = {
count: 0
}
componentDidMount () {
this.setState({
count: this.state.count + 1,
})
console.log('count1', this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log('count2', this.state.count);
}
render() {
return (
<div>
count的值{this.state.count}
</div>
)
}
}
// 打印:
// count1 0
// count2 0
例3 setTimeout中的setState
import React from 'react';
export default class SetState extends React.Component {
constructor(props) {
super(props);
}
state = {
count: 0
}
componentDidMount () {
setTimeout(() => {
this.setState({
count: this.state.count + 1,
})
console.log('count1', this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log('count2', this.state.count);
}, 0);
}
render() {
return (
<div>
count的值{this.state.count}
</div>
)
}
}
// 打印:
// count1 1
// count2 2
例4 Promise中的setState
import React from 'react';
export default class SetState extends React.Component {
constructor(props) {
super(props);
}
state = {
count: 0
}
componentDidMount () {
Promise.resolve()
.then(() => {
this.setState({
count: this.state.count + 1,
})
console.log('count1', this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log('count2', this.state.count);
})
}
render() {
return (
<div>
count的值{this.state.count}
</div>
)
}
}
// 打印:
// count1 1
// count2 2
从例1和例2的输出结果来看, 在setState后直接取state的值发现并没有更新, setState对state的更新似乎是个异步的过程;
而从例3, 例4输出结果来看, setState又是一个同步更新state的操作, 可以立即拿到更新的结果.
也就是说, setState有的时候是异步的有的时候是同步的, 真是非常的魔性. 根据网上的一些文章和自己的实验可以得出如下结论.
- 在合成事件, 生命周期函数中的setState是异步批量更新的, 不能立即拿到更新的结果, 多次setState只会走一次render
- 在setTimeOut, setInterval, 原生事件, Promise中的setState是同步逐个更新的, 可以立即拿到更新的state, 而且每次setState都会走一次render
关于是批量更新还是非批量更新可以在render函数中打印查看
setState魔性表现揭秘
理解setState的异步批量更新
下边是个异步批量更新的示意图
这里将在合成事件, setTimeout等中的写的代码的调用称为Main Process.
例如下边componentDidMount
中的代码的执行都叫Main process.
componentDidMount () {
this.setState({
count: this.state.count + 1,
});
console.log('count1', this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log('count2', this.state.count);
}
直接结合这段代码分析上边的这个看起来很牛x的图.
首先执行一个setState, 判断是setState操作, 创建一个更新任务加入更新队列, 交给协调中心, 协调中心判断不需要更新, 继续执行main Process中的代码.
遇到第一个console, 直接执行, 打印时取出了state, 显然state没更新还是原来的值, 然后再执行Main Process代码.
遇到第二个setState, 注意此时取出的state是没有更新的, 再创建一个更新任务到更新队列, 交给协调中心, 协调中心判断不需要更新, 继续执行main Process中的代码. 然后执行了console, 取出的state是没更新的.
一定时间后, 协调中心再次调度, 发现可以更新了, 然后执行了更新队列的两个任务, 得到一个新的state, 然后更新this.state
和视图.
从以上分析可以了解到为什么两个console打印的都是之前的值.
这里有一个黑盒, 协调中心怎么运行的, 这是以后需要研究的了, 目前尚不清楚, 可以猜测这里边应该有个setTimeout 或者类似setTimeout的东西.
理解setState的同步单个更新
下边是同步更新的示意图
这里还是结合一段代码来分析
import React from 'react';
export default class SetState extends React.Component {
constructor(props) {
super(props);
}
state = {
count: 0
}
click = () => {
setTimeout(() => {
this.setState({
count: this.state.count + 1,
})
console.log('count1', this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log('count2', this.state.count);
}, 0);
}
render() {
return (
<div onClick={this.click}>
count的值{this.state.count}
</div>
)
}
}
首先遇到第一个setState, 判断是setState, 创建一个更新任务到更新队列, 然后进入协调中心, 协调中心通过某种手段判断出需要同步更新, 直接执行更新队列的任务, 得到新的state, 然后更新视图, 继续执行Main Process中的代码.
遇到console, 直接执行, 取出state(注意是更新了的)答应.
然后又遇到setState(注意这里拿到的state是更新了的), 创建更新任务进入更新队列, 然后进入协调中心, 协调中心通过某种手段判断出需要同步更新, 直接执行更新队列的任务, 得到新的state, 然后更新视图, 继续执行Main Process中的代码.
再次遇到console, 直接执行, 取出state(注意是二次了的)答应.
从以上分析可以看出同步setState
为什么是同步的, 原因就在于他没有一个异步判断过程, 直接更新了state.
几点待解决的问题
- 协调中心是什么时候, 如何判断出需要更新的
- 协调中心是如何识别是一个setState是在setTimeout还是在合成事件亦或生命周期等过程中的.
彩蛋
说一下阅读react源码的感受, 最开始直接看src目录, react部分还行, 比较容易.
但是到了react-dom
就不行了, 各种调用, 各种乱七八糟的东西, 有时跟着函数调用跳来跳去, 结果最开始想干嘛的都忘了, 这样读起来真的很打击人.
其实读源码更多不是了解其代码组织方法, 而是了解核心原理.
下边是几个小建议:
- 带着问题读源码, 尤其是开始读的时候, 如果漫无目的的读, 会很没有成就感, 甚至是强烈的挫败感, 读了半天也不知道学到了什么
react-dom
的src代码组织十分复杂, 建议直接读开发版的编译产物, 都在一个文件里, 比较容易找.- 多用断点, 可以直接在开发版编译产物打断点看, 非常方便
- 不要纠结太多细节, 要抱有不求甚解的态度, 不懂的地方可以暂时放过
小结
setState是一个容易让人困惑的东西, 尤其对react初学者来说, 可能感觉有点琢磨不透. 本文结合源码和自己的理解对setState的同步异步机制做了一些分析. 有些地方可能并不是十分准确, 但希望能帮助对setState同步异步机制困惑的朋友理解一些其中的原理. 最后需要记忆一下什么场景是同步更新, 什么场景是异步更新, 这个是写代码能实实在在用的到的.
理解setState的更多相关文章
- react面试题——理解setState(源码object.assign)
setState是异步的方式 this.setState({ counter:this.state.counter+1 }) console.log(this.state.counter) s ...
- 重谈react优势——react技术栈回顾
react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ...
- React-setState的那些事儿
关于setState,使用过react的人应该再熟悉不过了,在hooks还不那么普及的时候,除了使用函数式组件,我们使用最多的应该就是类创建react的组件了,而在类组件中我们通常会使用state来管 ...
- 深入理解 React JS 中的 setState
此文主要探讨了 React JS 中的 setState 背后的机制,供深入学习 React 研究之用. 在课程 React.js入门基础与案例开发 中,有些同学会发现 React JS 中的 set ...
- react中的setState的使用和深入理解
前端框架从MVC过渡到MVVM.从DOM操作到数据驱动,一直在不断的进步着,提升着, angular中用的是watcher对象,vue是观察者模式,react就是state了,他们各有各的特点,没有好 ...
- 对于react中的this.setState的理解
一.this.setState第二个参数的作用:修改数据更新后最新的DOM结构 二.this.setState为什么是异步的? 1.setState只在合成事件和钩子函数中是“异步”的,在原生事件和s ...
- React 的setState 理解
我们都知道在React中,setState() 方法是用来改变组件状态的,在项目中也一直用,也没有出现什么问题(使用方法太简单了),但今天看了一篇文章,提到了setState 使用时的两个注意点,加深 ...
- react中this.setState的理解
this.setState作用? 在react中要修改this.state要使用this.setState,因为this.state只是一个对象,单纯的修改state并不会触发ui更新.所以我们需要用 ...
- 多线程锁--怎么理解Condition
在java.util.concurrent包中,有两个很特殊的工具类,Condition和ReentrantLock,使用过的人都知道,ReentrantLock(重入锁)是jdk的concurren ...
随机推荐
- WIN10家庭版桌面右键单击显示设置出现ms-settings:display或ms-settings:personalization-background解决办法[原创]
最近,笔者的笔记本卸载oracle数据库,注册表里面删除了不少相关信息,没想到担心的事情还是来了!桌面右键单击显示设置出现ms-settings:display或ms-settings:persona ...
- spring-boot-plus V1.2.1 发布 文件上传下载和静态资源访问
[V1.2.1-RELEASE] 2019.08.21 ⭐️ New Features 文件上传保存到服务器指定目录 文件下载 访问上传的图片等资源 启用项目静态资源访问,可访问static/temp ...
- 关于Python虚拟环境与包管理你应该知道的事
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- Spring Context 你真的懂了吗
今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识. 1. context 是什么 我们经常在编程中见到 contex ...
- 【朝花夕拾】Handler篇(二)
前言 一年前写过一篇文章[朝花夕拾]Handler篇,随着这一年来对Handler更多的认识和理解,本文对Handler知识点做的一些补充. 一.为什么要引入Handler Handler的主要作用是 ...
- helm部署Redis哨兵集群
介绍 Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,可以实现故障发现.故障自动转移.配置中心和客户端通知. 如下图: Redis Sentinel 故障转移过程: 从这 ...
- C#开发BIMFACE系列9 服务端API之获取应用支持的文件类型
系列目录 [已更新最新开发文章,点击查看详细] BIMFACE最核心能力之一是工程文件格式转换.无需安装插件,支持数十种工程文件格式在云端转换,完整保留原始文件信息.开发者将告别原始文件解析烦 ...
- python编写排列组合,密码生产功能
python编写排列组合 python在编写排列组合是会用到 itertools 模块 排列 import itertools mylist = list(itertools.permutation ...
- 牛客小白月赛6 水题 求n!在m进制下末尾0的个数 数论
链接:https://www.nowcoder.com/acm/contest/135/C来源:牛客网 题目描述 其中,f(1)=1;f(2)=1;Z皇后的方案数:即在Z×Z的棋盘上放置Z个皇后,使其 ...
- 51nod 1218 最长递增子序列 V2(dp + 思维)
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1218 题解:先要确定这些点是不是属于最长递增序列然后再确定这 ...