useState执行机制

news/2024/7/15 18:28:11 标签: react.js, javascript, 前端

使用 react hooks 很久了,对于 useState 的理解一直都是模糊不清,终于下定决心把它理清楚,看似简单的 useState 暗藏玄机,先来看一段代码

javascript">import { useState } from 'react';

console.log('函数外');
const HomePage = () => {
	console.log('函数内 顶部');
	const [num, setNumber] = useState(0);
	const [count, setCount] = useState(0);

	const handerNum = () => {
		for (let i = 0; i < 5; i++) {
			console.warn(`${i}次执行`);
			setTimeout(() => {
				console.log('set number 前', num);
				setNumber((state) => {
					console.log('set number 内', state, state + 1);
					return state + 1;
				});
				console.log('set number 后', num);
				console.log('---------------------------->');
			}, 1000);
			console.log('timer 后输出');
		}
	};
	
	const handerCount = () => {
		for (let i = 0; i < 5; i++) {
			console.warn(`${i}次执行`);
			setTimeout(() => {
				console.log('set count 前', count);
				setCount(count + 1);
				console.log('set number 后', count);
				console.log('---------------------------->');
			}, 1000);
			console.log('timer 后输出');
		}
	};
	
	console.log(`return前, num: ${num}, count: ${count}`);
	
	return (
		<div>
			<Button onClick={handerNum}>number: {num}</Button>
			<hr />
			<Button onClick={handerCount}>count: {count}</Button>
		</div>
	);
};

export default HomePage;

这就是一个普通的 jsx 文件,思考下面两个问题

  1. 当点击number按钮时输出什么
  2. 当点击count按钮时输出什么

下面我们揭晓答案
点击 number 按钮时输出如下
在这里插入图片描述
点击 count 按钮时输出如下
在这里插入图片描述
我们在 setNumber 中传入的是一个回调,在 setCount 中传入的是一个 表达式,同样是 setState 为什么传入表达式与传入函数的执行结果会有这么大差异(这里面的setState指的是setNumber 和 setCount)下面我们逐一解释handerNum的执行:

javascript">const handerNum = () => {
	for (let i = 0; i < 5; i++) {
		console.warn(`${i}次执行`);
		setTimeout(() => {
			console.log('set number 前', num);
			setNumber((state) => {
				console.log('set number 内', state, state + 1);
				return state + 1;
			});
			console.log('set number 后', num);
			console.log('---------------------------->');
		}, 1000);
		console.log('timer 后输出');
	}
};

点击 number 按钮如何执行的

我们知道 js 是顺序执行,当点击 number 按钮时,会调用 handerNum 方法,执行 for 循环,第一次for循环 执行,遇到console.warn(第${i}执行);直接输出 i 的值, 遇到setTimeout会将 setTimeout 放入定时器线程,并记录延迟时间,当延迟时间结束(1秒后),会把 setTimeout回调函数放入事件触发线程,主线程空闲时会调用事件触发线程中的回调函数,接着向下执行遇到console.log('timer 后输出'); 直接输出,第一次for循环结束,以此类推,for循环执行五次以后,定时器线程中就有5个 setTimeout ,定时器线程会根据延迟时间长短确定优先级,相同时间遵循先进先出原则,依次放入事件触发线程,等待主线程空闲调用。至此这个方法执行完毕,这里面有个疑点:为什么console.log('timer 后输出');的执行会在 setTimeout之前,因为 setTimeout是异步任务,总结下: 同步优先,异步靠边,回调垫底,这就是为什么下面红框输出会在一起
在这里插入图片描述
在说 setTimeout 回调之前我们先来说说 setState的执行顺序
当 react 工作流执行的时候(我们把react api 的执行叫react工作流),setState会判断传入的参数是函数还是值(值其实也是表达式,因为表达式最终会计算出具体值)参数类型不同执行的机制也有所差异,setState方法本身就是一个异步方法,分为两种 mountStateupdateState, 如果首次执行setState的时候,先执行 setState回调函数计算出值,然后立马调用组件方法(上面代码里组件方法为HomePage)在组件更新完成后,再执行 setState 后面的代码,这部分属于 mountState 再次执行setState时,react 会先调用组件方法(引起组件更新),接着执行setState的回调计算出更新值,执行渲染输出也就是return的reactDom,最后执行setState后面的代码
下面我们看控制台输出和上面描述是不是一样

接下来我们再看 setTimeout 回调的执行

javascript">() => {
	console.log('set number 前', num);
	setNumber((state) => {
		console.log('set number 内', state, state + 1);
		return state + 1;
	});
	console.log('set number 后', num);
	console.log('---------------------------->');
}

第一个 setTimeout 回调的执行,console.log('set number 前', num);控制台直接输出,往下遇到 setNumber 方法,(因为首次执行先执行setState)先执行传入的是回调,console.log直接输出set number 内 0 1 return 计算出值,调用组件函数(也就是HomePage)整个函数组件会重新执行,所以输出

函数内 顶部
return前, num: 1, count: 0

最后执行 setNumber 后面的代码 所以输出

set number 后 0
---------------------------->

第一个 定时器回调执行完成,也就是 mountState,完成
第二个 setTimeout 回调执行,console.log('set count 前', count);控制台输出

set number 前 0

setNumber 的回调会在调用组件方法之后,return之前执行,也就是在遇到 setNumber 会先重新调用下函数组件所以会输出

函数内 顶部

紧接着执行 setNumber 的回调,输出如下,return 最新state

set number 内 1 2

函数组件会使用最新的state做render输出,也就是执行函数组件中的 return 关键字,在执行 return 先执行 return 上的 console.log(return前, num: ${num}, count: ${count}); 输出如下

return, num: 2, count: 0

最后执行 setNumber 后面的代码输出

set number 后 0
index.tsx:35 ---------------------------->

剩下定时器回调执行同上
接下来我们整理下 setState 回调作为参数的执行顺序

mountState:
先调用回调方法计算出state ➞ 调用组件方法 ➞ return ➞ setState后面代码
updateState:
先调用组件方法 ➞ setState回调计算出新的state  ➞ return ➞  setState后面代码

接下来我们再看 setState 传入表达式的执行


http://www.niftyadmin.cn/n/1080877.html

相关文章

面试的时候被问如何看待加班,该怎么回答?

最近996.icu项目持续火爆了一段时间&#xff0c;虽然暂时还没有取得实质性的效果&#xff0c;但已经引发了人们的热议。由996我想起了另外一个相关的话题&#xff0c;就是如果你去面试时&#xff0c;面试官问你”如何看待加班“&#xff0c;你该怎么回答&#xff1f; 因为我自己…

2021前端高质量技术文章归纳

叙述 2021年培养了个好习惯,早上挤地铁的时候看一些技术文章,这一年看几百篇的技术文章,有的文章确实很水, 有的文章醍醐灌顶,有的文章至少读过四五遍,每遍都有新的感悟,总结在下面推荐大家阅读 回顾2021前端大事件总结 2021 年前端大事记 扫盲 Base64编码知识 深入理解浮…

人工神经网络基本构成有哪些?

人工神经网络的特点有哪些 人工神经网络的特点和优越性&#xff0c;主要表现在三个方面&#xff1a; 第一&#xff0c;具有自学习功能。例如实现图像识别时&#xff0c;只在先把许多不同的图像样板和对应的应识别的结果输入人工神经网络&#xff0c;网络就会通过自学习功能&am…

网络公司网站源码介绍Version1.0

年底了&#xff0c;公司不是很忙&#xff0c;就用三层做了网络公司的网站&#xff0c;现在把功能简单介绍下。 这个网站能满足日常中小型企业的需求。 前台&#xff0c;用户可以注册&#xff0c;进入个人中心&#xff0c;这里主要是为了以实现网上下订单打基础。 在线留言 网站…

TS中的类(class)

类(class): 定义了一切事物的抽象特点(属性和方法) 对象(object): 类的实例(通过 new 来生成) 面向对象特点: 封装,继承,多态 封装: 将操作数据的细节隐藏起来,只暴露接口, 调用者只需要通过接口来访问对象继承: 子类继承父类, 子类除了拥有父类的特性外还可以拥有更加具体的特…

人工神经网络的基本原理是什么

人工神经网络&#xff0c;人工神经网络是什么意思 一、 人工神经网络的概念。 人工神经网络&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;简称神经网络(NN)&#xff0c;是基于生物学中神经网络的基本原理&#xff0c;在理解和抽象了人脑结构和外界刺激响…

高级SQL优化(三) 常用优化工具 ——《12年资深DBA教你Oracle开发与优化——性能优化部分》...

目录&#xff1a; Oracle数据完整性和锁机制 索引及优化之表分析 表分析、约束及表间关系 Oracle体系结构1Oracle体系结构2 海量数据库及分区1 海量数据库及分区2 海量数据库及分区3 海量数据库及分区4 高级SQL优化(一) 高级SQL优化(二) 高级SQL优化(三) 常用优化工具 PPT和源…

简述人工神经网络的作用,人工神经网络的基本概念

什么是人工神经网络? 人工神经网络就是&#xff0c;人工智能模拟人体内的神经系统的工作原理和工作结构进行模拟的一套人工系统网络快速有效的传递信息。 什么是人工神经网络&#xff1f; 人工神经网络&#xff08;ANNs&#xff09;也叫神经网络&#xff08;NNs&#xff09;或…