React16源码: React中调度之batchedUpdates的源码实现

batchedUpdates


1 )概述

  • requestWork 在中间, 会判断一个 isBatchingUpdates 做一些特定的操作
  • batchedUpdates 是一个批量更新的操作, 什么是批量更新呢?先来看一个例子

2 )示例

index.js

import React from 'react'
import ReactDOM from 'react-dom'import App from './demos/batchedUpdates'ReactDOM.render(<App />, document.getElementById('root'))

demos/batchedUpdates

import React from 'react'
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom'export default class BatchedDemo extends React.Component {state = {number: 0,}handleClick = () => {// 第一种场景 事件处理函数自带`batchedUpdates`// this.countNumber()// 第二种场景,主动`batchedUpdates`// setTimeout(() => {//   this.countNumber()// }, 0)// 第三种场景,setTimeout中没有`batchedUpdates`setTimeout(() => {batchedUpdates(() => this.countNumber())}, 0)}countNumber() {const num = this.state.numberthis.setState({number: num + 1,})console.log(this.state.number)this.setState({number: num + 2,})console.log(this.state.number)this.setState({number: num + 3,})console.log(this.state.number)}render() {return <button onClick={this.handleClick}>Num: {this.state.number}</button>}
}
  • 在上述 countNumber 中定义了更改 state的方法
  • 当点击 按钮时,里面的 num 会更改,回调函数是 handleClick
    • 第一种情况
      • 当直接调用 countNumber 时,页面显示 3,但是 console 面板分别输出 3次0
      • 注意 只获取一次 num, 初始化是 0,执行了3次setState, 最后一次是 0 + 3 = 3
      • 这是因为 batchUpdtes 的概念所导致
    • 第二种情况
      • 在一个延迟器中调用 countNumber
      • 这时候页面显示 3,console面板中分别输出 1, 2, 3
      • 这是我们符合预期的输出
    • 第三种情况
      • 在延迟器中使用 batchedUpdates 这个 API,来调用 countNumber
      • 这时候,页面显示 3,console面板 依次输出 3次0
  • 那么它(batchedUpdates)的原理是什么呢?

3 ) 源码

进入到 requestWork 函数中的相关判断,可以在源码 nodemodules/react-dom/cjs/react-dom-development.js 中
找到 requestWork 进行debugger,重启 react项目,之后在 Chrome 中调试

if (isBatchingUpdates) {// Flush work at the end of the batch.if (isUnbatchingUpdates) {// ...unless we're inside unbatchedUpdates, in which case we should// flush it now.nextFlushedRoot = root;nextFlushedExpirationTime = Sync;performWorkOnRoot(root, Sync, true);}return;
}
  • 第一种场景下

    • isBatchingUpdates 是 true, isUnbatchingUpdates 是 false
    • 这样,最终直接 return 了, 就没有进入后续的调度,就什么都没做
    • 通过调用堆栈可知,3个 update 调用完成后,存入 updateQueue之后
    • 会调用 performSyncWork 来帮助我们完成任务调度的过程,而非通过 requestWork 来完成的任务调度
    • 这样,任务更新队列就在一起把state更新了, 所以要等到 state 所有都变掉之后,才会执行state的更新
    • 所以 console 面板中打印的,不是真正更新后的 state
    • 这就是 batchUpdates 让我们在一个方法调用里面,所有的setState都是一次性更新到我们的state里面的
    • 而不是每次都调用 setState 进入调度和渲染,以此来提升性能效率
  • 第二种场景下

    • 当 setTimeout 执行后,方法执行上下文已不是 handleClick这个函数了,而是 window 了
    • 这时的 batchUpdates 已重置了
    • 这时候调用 countNumber 就没有 isBatchingUpdates 为 true 的情况了,就不会批量执行
    • 所以,每次 setState 都会更新,导致我们每次打印的都是新的 state
    • 这种会导致,我们的应用性能变得很低,所以这种是极不推荐的
  • 第三种场景下

    • 基于 batchedUpdates 的 API 调用 countNumber
    • 这个 API 的作用是 设置上下文,handleClick 是react里面的事件体系产生的回调
    • 事件体系会主动调用 batchedUpdatesinteractiveUpdates 帮助我们设置上下文
    • 所以,在react事件中大部分回调都是会帮助我们做批量更新的
    • 而不会让我们在事件的回调中做多次的更新
    • 这是react 事件绑定中的性能优化

在 ReactFiberSchduler.js 中的 batchedUpdates

// TODO: Batching should be implemented at the renderer level, not inside
// the reconciler.
function batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {const previousIsBatchingUpdates = isBatchingUpdates;isBatchingUpdates = true;try {return fn(a);} finally {isBatchingUpdates = previousIsBatchingUpdates;if (!isBatchingUpdates && !isRendering) {performSyncWork();}}
}
  • 它设置 isBatchingUpdates = true;,在设置之前记录前值 const previousIsBatchingUpdates = isBatchingUpdates;
  • 当我们的回调 fn 被调用之后,在 finally 中 恢复 isBatchingUpdates 成之前的值 previousIsBatchingUpdates
  • 之后,判断是否符合 !isBatchingUpdates && !isRendering 符合了,则调用 performSyncWork 方法

同样,unbatchedUpdates 也是来自这个文件

// TODO: Batching should be implemented at the renderer level, not inside
// the reconciler.
function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {if (isBatchingUpdates && !isUnbatchingUpdates) {isUnbatchingUpdates = true;try {return fn(a);} finally {isUnbatchingUpdates = false;}}return fn(a);
}
  • 它设置 isUnbatchingUpdates 为 true
  • 它也是上下文的关系,让我们在调用 fn 的时候有这么一个值是 true 的上下文
  • 标记产生的不一样的变化
  • 以上是批量更新的概念和原理
  • 扩展
    • 从上面我们知道 setState 这个方法是同步的,
    • 但是调用之后,并不标志 react 的状态立马更新
    • 这个更新是需要根据当前环境的上下文来判断的
    • 如果处于批量更新的情况下,state不是立马更新的
    • 如果不处于批量更新的情况下,state 可能会立马更新
      • 因为 有 ConcurrentMode 这种异步渲染的情况
      • 这种情况下,state 也不是立马就更新的
    • 进入异步更新,就会进入到异步调度的过程中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/623173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】mysql集群

文章目录 一、mysql日志错误日志查询日志二进制日志慢查询日志redo log和undo log 二、mysql集群主从复制原理介绍配置命令 读写分离原理介绍配置命令 三、mysql分库分表垂直拆分水平拆分 一、mysql日志 MySQL日志 是记录 MySQL 数据库系统运行过程中不同事件和操作的信息的文件…

二级C语言备考6

一、单选 共40题 &#xff08;共计40分&#xff09; 第1题 &#xff08;1.0分&#xff09; 题号:6675 难度:易 第1章 以下选项中叙述正确的是 A:C程序中的语句要经过编译才能转换成二进制机器指令 B:算法需要包含所有三种基本结构 C:有些算法不能用…

Mac M1 Parallels CentOS7.9 Rancher + K8S + Gitlab + Jenkins +Harbor CICD

一、资源清单 机器名称IP地址角色k8srancher高可用部署: https://blog.csdn.net/qq_41594280/article/details/135312148rancher10.211.55.200管理K8S集群k8svip10.211.55.199K8S VIPmaster0110.211.55.201K8S集群主节点master0210.211.55.202K8S集群主节点master0310.211.55.…

华为机试真题实战应用【赛题代码篇】-分苹果(附Java、C++和python代码)

目录 题目描述 解析思路 思路1 思路2 代码实现 Java 代码2

redis数据结构源码分析——跳表zset

文章目录 跳表的基本思想特点节点与结构跳跃表节点zskiplistNode属性 跳跃表链表属性 跳表的设计思想和优势API解析zslCreate&#xff08;创建跳跃表&#xff09;zslCreateNode&#xff08;创建节点&#xff09;zslGetRank&#xff08;查找排位&#xff09;zslDelete&#xff0…

Tiktok/抖音旋转验证码识别

一、引言 在数字世界的飞速发展中&#xff0c;安全防护成为了一个不容忽视的课题。Tiktok/抖音&#xff0c;作为全球最大的短视频平台之一&#xff0c;每天都有数以亿计的用户活跃在其平台上。为了保护用户的账号安全&#xff0c;Tiktok/抖音引入了一种名为“旋转验证码”的安…

flex布局之美,以后就靠它来布局了

写在前面 在很久很久以前&#xff0c;网页布局基本上通过table 元素来实现。通过操作table 中单元格的align 和valign可以实现水平垂直居中等 再后来&#xff0c;由于CSS 不断完善&#xff0c;便演变出了&#xff1a;标准文档流、浮动布局和定位布局 3种布局 来实现水平垂直居…

恒源云GPU服务器使用Linux图形化界面

编程如画&#xff0c;我是panda&#xff01; 干货满满&#xff0c;不要走开~ 前言 前一节分享了如何在GPU云服务器上创建实例并运行YOLOV5项目&#xff0c;但是使用命令行的方式容易劝退很多小白&#xff0c;并且有些环境配置是需要图形化界面的&#xff0c;所以这一节就教大家…

Redis常见命令、数据类型

我们可以通过Redis的中文文档&#xff1a;Redis命令中心&#xff08;Redis commands&#xff09; -- Redis中国用户组&#xff08;CRUG&#xff09;&#xff0c;来学习各种命令。 也可以通过菜鸟教程官网来学习&#xff1a;Redis 键(key) | 菜鸟教程 一、Redis数据结构介绍 Red…

Koa学习笔记

1、npm 初始化 npm init -y生成 package.json 文件,记录项目的依赖2、git 初始化 git init生成 .git 隐藏文件夹,.git 的本地仓库创建 .gitignore 文件,添加不提交文件的名称3、创建 ReadMe.md 文件 记录项目笔记4、搭建项目 安装 Koa 框架npm install koa5、编写最基本的…

对Transformer的理解。

要理解Transformer&#xff0c;需要先理解注意力机制&#xff0c;下面大部分内容来自台大教授李宏毅老师讲课资料。 注意力机制 之前使用的MLP&#xff0c;CNN&#xff0c;RNN模型可以解决一些简单序列问题&#xff0c;但当序列长度太长容易失去效果&#xff0c;原因是看了新…

xtu oj 1169 最大子段和

题目描述 给你一个数列a1,a2,...,an,求m个连续数字组成的子段和最大值。 输入 有多个样例&#xff0c;每个样例的第一行是两个整数n和m&#xff0c;(1≤m≤n;≤100,000)。如果n和m为0表示输入结束&#xff0c;这个样例不需要处理。第二行是n个整数ai&#xff0c;0≤ai≤1000…

精确掌控并发:固定时间窗口算法在分布式环境下并发流量控制的设计与实现

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;14&#xff09;篇。点击上方关注&#xff0c;深入了解支付系统的方方面面。 本篇主要介绍分布式场景下常用的并发流量控制方案&#xff0c;包括固定时间窗口、滑动时间窗口、漏桶、令牌桶、分布式消息中间件…

力扣每日一练(24-1-14)

做过类似的题&#xff0c;一眼就是双指针&#xff0c;刚好也就是题解。 if not nums:return 0p1 0 for p2 in range(1, len(nums)):if nums[p2] ! nums[p1]:p1 1nums[p1] nums[p2]return p1 1 根据规律&#xff0c;重复的数字必定相连&#xff0c;那么只要下一个数字与上一…

Fluent 动网格应用:2.5D 网格重构

1 概述 2.5D 网格重构是一种快速网格重构方法&#xff0c;主要应用于涡旋压缩机等存在复杂平面运动且无法简化为二维计算的问题。 涡旋压缩机工作原理&#xff08;视频源&#xff1a;维基百科&#xff09; 适用于 2.5D 动网格的问题特点&#xff1a; 计算域几何形状为柱体类形…

八. 实战:CUDA-BEVFusion部署分析-导出带有spconv的SCN网络的onnx

目录 前言0. 简述1. 使用spconv进行SCN的推理测试2. 导出onnx3. 补充-装饰器钩子函数总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习下课程第八章——实战&#x…

反向代理+web集群+mysql mha实验总结

一、实验步骤 1、部署框架前准备工作 服务器类型部署组件ip地址DR1调度服务器 主&#xff08;ha01&#xff09;KeepalivedLVS-DR192.168.86.13DR2调度服务器 备 (ha02)KeepalivedLVS-DR192.168.86.14web1节点服务器 (slave01)NginxTomcatMySQL 备MHA managerMHA node192.168.8…

cmake 中的set用法

可以后面跟一串字符串 set — CMake 3.0.2 Documentation

esxi-vSphere

esxi安装 vCenterServer 安装 给予 esxi,一般一个esxi &#xff0c;就安装一个 vCenter 关于 vCenter Server 安装和设置 vSphere Client安装 软件下载 VMware vSphere 8.0 download: 百度网盘链接&#xff1a;百度网盘 请输入提取码 链接: https://pan.baidu.com/s/1juyKl…

【Java SE语法篇】9.抽象类和接口

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ 文章目录 1. 抽象类1.1 抽象类的概念1.2 抽象类的语法1.3 抽象…