React中的setState是异步的吗?
在React中更新状态,一般的写法都是this.setState({a:1})
,而非Vue那样this.a = 1
。并且,如果在如下场景:
state = {
count: 0,
};
componentDidMount() {
this.setState({
count: this.state.count + 1,
});
console.log("console: " + this.state.count);
}
render(){
console.log("render: " + this.state.count);
}
打印结果为
console:0
render:1
所以,从表现上看,setState
是异步的。
其实,这只是React的障眼法。
setState
是同步执行的!但是state并不一定会同步更新(异步更新和同步更新都存在)
1. 对比Vue
Vue的数据更新是基于event-loop
机制(是更新,不是数据双向绑定)。
image.png
Vue在侦听到数据变化后,会开启一个队列,并缓冲在同一事件循环中发生的所有数据变更(如果同一个 watcher 被多次触发,只会被推入到队列中一次)。然后,在下一个事件循环Tick/微任务中,Vue 刷新队列执行实际工作。(事件循环参考《宏任务和微任务到底是什么?》)
2. React.setState()
中的异步更新
setState()
中有个特别重要的布尔属性isBatchingUpdates
(默认为false,),它决定了state
是同步更新还是异步更新。
调用栈如下(涉及到React事务机制,可以参考文章《React进阶篇(四)事务》):
setState调用.png
setState
只在合成事件和钩子函数中是“异步更新”的。 异步更新的背后,是同步代码处理("合成事件和钩子函数"的调用在"更新"之前)。 异步是为了实现批量更新的手段,也是React性能优化的一种方式。
2. React.setState()
中的同步更新
当然,也是有办法同步获取state
更新后的值:
-
setTimeout
等异步操作中调用setState函数 - DOM原生事件
- 利用
setState
回调函数 - 函数式
setState
用法
前两个都比较好理解,因为没有前置的batchedUpdate
调用,所以isBatchingUpdates
为false。不会开启批量更新模式,那么,在上面的调用栈图示里面,会直接走到事务更新。
后面两个方法,是React本身提供的。要注意的是,setState
回调函数要在render函数被重新执行后才执行。
下面有一道题目,试试做吧!
import React from "react";
class TestView extends React.Component {
state = {
count: 0,
};
componentDidMount() {
this.setState({
count: this.state.count + 1,
});
console.log("console: " + this.state.count);
this.setState({ count: this.state.count + 1 }, () => {
console.log("console from callback: " + this.state.count);
});
this.setState(
(prevState) => {
console.log("console from func: " + prevState.count);
return {
count: prevState.count + 1,
};
},
() => {
console.log("last console: " + this.state.count);
}
);
}
render() {
console.log("render" + this.state.count);
return <h4>test</h4>;
}
}
export default TestView;
- 如何使用curl调试openstack的api
- selenium+python自动化81-报告优化
- Selenium+python自动化82-只截某个元素的图
- libvirt-TLS加密
- 在openstck中配置使用cloud-init
- libvirt-使用cgroup做资源分割控制
- libvirt-usb设备透传给虚拟机
- Appium+python自动化22-Android夜神模拟器
- 每周学点大数据 | No.9递归——以阶乘为例
- appium+python自动化24-滑动方法封装(swipe)
- libvirt-虚拟机qos控制
- 【深度】Deep Visualization:可视化并理解CNN
- Appium+python自动化28-name定位
- Appium+python自动化29-toast消息
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Sony-PMCA-RE, 反向工程索尼PlayMemories相机应用
- 中标麒麟系统Your trial is EXPIRED and no VALID licens
- R初探
- 独家 | 在Python编程面试前需要学会的10个算法(附代码)
- 为什么一个还没毕业的大学生能够把 IO 讲的这么好?
- 如何将单 master 升级为多 master 集群
- 为 Kubernetes 节点发布扩展资源
- Kubernetes 资源预留配置
- PHP一些常见的漏洞梳理
- File的基本操作
- 一文教你在Colab上使用TPU训练模型
- 如何在Docker容器中运行Docker [3种方法]
- [漏洞复现] 三.CVE-2020-0601微软证书漏洞及Windows验证机制欺骗复现
- PDF文件密码破解
- Ubuntu 制作离线源