React源码解析18(11)------ 实现多次setState的批处理
摘要
在React中,如果涉及到了多次setState,组件render几次。setState是同步的还是异步的。这是一个很常见的面试题。
而本篇文章,就是主要实现React中,对于这部分的性能优化,我们称之为批处理。例如当我有下面的JSX。
const root = document.querySelector('#root');function App() {const [num, setNum] = useState(0)const click1 = () => {setNum(num + 1)setNum(num + 2)setNum(num + 3)}return jsx("div", {onClick: click1,children: num});
}ReactDOM.createRoot(root).render(<App />)
对于当前的点击事件来说,只有最后的setNum(num + 3)是有效的。
但是在我们之前的实现中,对于这种连续三次setState,我们的代码就要处理三次,就要经过三次beginWork,completeWork,commitWork。
那我们能不能实现出一种方式,对于这种多次setState,最终之做最后一次处理。
1.修改update相关逻辑
目前,在我们的代码里,能够让组件更新的方式,就是通过setState,触发更新。而updateQueue就是用来保存更新的内容。
function enqueueUpdate(updateQueue, update) {updateQueue.shared.pending = update
}
之前,在enqueueUpdate方法里,我们是直接进行赋值的。这没什么问题,因为之前每次赋值之后都会更新一下。
但是现在我们希望,最后只更新一次的话。我们就需要一个数据结构,可以保存多次更新的内容。这里使用的是链表结构,但在真正的React源码中,使用的是环形链表。
function enqueueUpdate(updateQueue, update) {// updateQueue.shared.pending = updatelet pending = updateQueue.shared.pending;if(pending === null) {updateQueue.shared.pending = update}else{while(pending.next != null) {pending = pending.next;}pending.next = updatepending = pending.next;}
}
OK,修改完之后,我们调用了三次setState,那么updateQueue中保存的应该是一个链表了。
在之前的processUpdateQueue方法里,也是直接更新就完了。但是现在updateQueue的结构发生了变化,所以对于processUpdateQueue,更新逻辑也要改变。
function processUpdateQueue(baseState, pendingUpdate) {const result = {memoizedState: baseState}if(pendingUpdate.next === undefined) {const action = pendingUpdate.action;//setState(() => {}) 传入方法if(typeof action === 'function'){result.memoizedState = action(baseState);}else {//setState()result.memoizedState = action;}return result}while(pendingUpdate != null) {const action = pendingUpdate.action;//setState(() => {}) 传入方法if(typeof action === 'function'){baseState = action(baseState);}else {//setState()baseState = action;}pendingUpdate = pendingUpdate.next;}result.memoizedState = baseState;return result;
}
我们需要遍历pending,将所有的更新内容返回。
2.修改workLoop循环
我们想一下,之前触发更新后,执行的机制是什么样子的。
- 更新updateQueue
- 拿到更新的updateQueue,挂载Hook上
- 执行workLoop
也就是说,每次workLoop都只能拿到当前更新的内容。
如果我希望在第一次workLoop就可以拿到所有的更新内容,并且取消后面的workLoop。
有什么方法呢?微任务!!!!
我们可以将执行workLoop的过程放在微任务里,这样执行workLoop的时候,updateQueue的链表已经生成。
同时我们用一个标志位,取消后面的workLoop执行。
当workLoop执行完成后,再将标志位置反。
let isFinished = false;
export function syncWorkLoop(root,hostRootFilber) {if(isFinished) {return;}Promise.resolve(null).then(() => {wookLoop(root,hostRootFilber);})isFinished = true;`在这里插入代码片`
}
const wookLoop = (root,hostRootFilber) => {//其他代码。。。。。isFinished = false;
}
3.修改filberHook
最后,我们只要在filberHook中触发的逻辑里,替换workLoop即可:
function disaptchState(filber, hook, action) {const update = createUpdate(action);enqueueUpdate(hook.updateQueue, update);workUpdateHook = hook;syncWorkLoop(filber.return.stateNode);
}
通过这种方式,当我执行多次setState,最终只会render一次。
相关文章:
React源码解析18(11)------ 实现多次setState的批处理
摘要 在React中,如果涉及到了多次setState,组件render几次。setState是同步的还是异步的。这是一个很常见的面试题。 而本篇文章,就是主要实现React中,对于这部分的性能优化,我们称之为批处理。例如当我有下面的JSX。…...

评测凯迪仕K70「千里眼」智能锁:不忘安全初心,便捷体验更上一层
能打败凯迪仕的,只有它自己。这是我们在体验过凯迪仕最新旗舰产品K70「千里眼」智能锁之后的感受。作为凯迪仕2023年最新旗舰机型,K70「千里眼」智能锁在配置上可以说是「机皇」般的存在。3K超高清智能锁猫眼、车规级24GHz雷达、大小双屏设计、三方可视对…...

mysql数据库root密码遗忘后,修改root密码
目录 方式一: 方式二: 2.1 也可以像我这样,普通用户登录进去后 2.2 执行如下命令,将已知的user1的加密密文更新到root中 2.3 查询数据库 2.4 用root用户登录 2.5 登录正常,但这会root登录进去后,无法…...

网络安全(黑客)快速入门~
网络安全的学习需要遵守循序渐进,由浅入深。 通常网络安全学习方法有两种: 方法1:先学习编程,然后学习Web渗透及工具使用等; 适用人群:有一定的代码基础的小伙伴 基础部分 基础部分需要学习以下内容&am…...

华为OD机试 - 数字颠倒(Java 2023 B卷 100分)
目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、Java算法源码投机取巧七、效果展示 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷&am…...
leetcode做题笔记87扰乱字符串
使用下面描述的算法可以扰乱字符串 s 得到字符串 t : 如果字符串的长度为 1 ,算法停止如果字符串的长度 > 1 ,执行下述步骤: 在一个随机下标处将字符串分割成两个非空的子字符串。即,如果已知字符串 s ,…...

第一章 初识Linux(含VMware安装Ubuntu、CentOS、Windows、FinalShell、快照)
目录 一、 课程的介绍 1.为什么要学习Linux 2.课程的安排 3.如何学习Linux 二、操作系统概述 1.学习目标 2.计算机的硬件和软件 3.什么是操作系统 4.常见的操作系统 5.本小节的总结 三、初识Linux 1.学习目标 2.Linux的诞生 3.Linux的内核 …...
MATLAB算法实战应用案例精讲-【图像处理】OCR识别方法-CRNN
目录 OCR综述 什么是OCR OCR发展历程 OCR 常用检测方法 基于回归的方法 1) box回归...

无涯教程-PHP - preg_grep()函数
preg_grep() - 语法 array preg_grep ( string $pattern, array $input [, int $flags] ); 返回由与给定模式匹配的输入数组元素组成的数组。 如果将flag设置为PREG_GREP_INVERT,则此函数返回输入数组中与给定模式不匹配的元素。 preg_grep() - 返回值 返回使用…...
【Linux】Nginx解决跨域问题
文章目录 一、跨域问题二、解决跨域问题三、结尾 一、跨域问题 在前后端分离的项目中,前端通常运行在一个域名或端口上,而后端运行在另一个域名或端口上。当浏览器发起跨域请求时,即前端页面向后端发送请求的域名、端口或协议与当前页面的域…...

无涯教程-PHP - preg_split()函数
preg_split() - 语法 array preg_split (string pattern, string string [, int limit [, int flags]]); preg_split()函数的操作与split()完全相同,只不过正则表达式被接受为pattern的输入参数。 如果指定了可选的输入参数limit,则仅返回子字符串的限…...
B. Spreadsheets
Problem - B - Codeforces 问题描述:excel有两种情况, Rr_nCc_n:R行数C列数ZZZ(列数)行数。 对这两个进行相互转换。 细节: 准确判断这两种情况 string str; cin>>str; auto posR str.find("R"), posC st…...

matlab面向对象
一、面向对象编程 1.1 面向过程与面向对象 区别: 面向过程的核心是一系列函数,执行过程是依次使用每个函数面向对象的核心是对象(类)及其属性、方法,每个对象根据需求执行自己的方法以解决问题 对象:单个…...

01、Cannot resolve MVC View ‘xxxxx前端页面‘
Cannot resolve MVC View ‘xxxxx前端页面’ 没有找到对应的mvc的前端页面。 代码:前端这里引入了 thymeleaf 模板 解决: 需要添加 thymeleaf 的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>s…...

时空智友企业流程化管控系统文件上传漏洞复现
0x01 产品简介 时空智友企业流程化管控系统是一个功能丰富、灵活可定制的企业管理工具。通过该系统,企业能够实现流程的自动化、协同的提升、数据的洞察和决策的优化,从而提高工作效率、管理水平和企业竞争力。 0x02 漏洞概述 时空智友企业流程化管控系…...

【已解决】Authenticator:无法添加账户请验证激活代码是否正确以及您的设备是否已为此应用启用推送通知
问题: 小米手机的Authenticator添加微软账户扫描QR码提示:无法添加账户请验证激活代码是否正确以及您的设备是否已为此应用启用推送通知 解决办法: 1、在通知管理中允许Authenticator所有通知。 2、在手机设置-账户与同步里找到谷歌基础服…...
聊聊springboot tomcat的maxHttpFormPostSize
序 本文主要研究一下spring boot tomcat的maxHttpFormPostSize参数 parseParameters tomcat-embed-core-9.0.37-sources.jar!/org/apache/catalina/connector/Request.java /*** Parse request parameters.*/protected void parseParameters() {parametersParsed true;Para…...

java并发:synchronized锁详解
背景: 在java多线程当中,我们总有遇到过多个线程操作一个共享数据时,而这个最后的代码执行结果并没有按照我们的预期一样得到正确的结果。此时我们就需要让代码执行在操作共享变量时,要等一个线程操作完毕时,另一个线程…...

Unity 之NavMeshAgent 组件(导航和路径寻找的组件)
文章目录 **作用**:**属性和方法**:**用途**:**注意事项**: NavMeshAgent 是Unity引擎中用于导航和路径寻找的组件。它可以使游戏对象在场景中自动找到可行走的路径,并在避免障碍物的情况下移动到目标位置。 以下是关于…...

装箱和拆箱
1. 概念 装箱 将值类型转换成等价的引用类型 装箱的步骤 拆箱 将一个已装箱的引用类型转换为值类型,拆箱操作需要声明拆箱后转换的类型 拆箱的步骤 1)获取已装箱的对象的地址 2)将值从堆上的对象中复制到堆栈上的值变量中 2. 总结 装箱和拆箱…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...