第六十周总结——React数据管理
React数据管理
代码仓库
React批量更新
React中的批量更新就是将多次更新合并处理,最终只渲染一次,来获得更好的性能。
React18版本之前的批量更新
// react 17 react-dom 17 react-scripts 4.0.3
import * as ReactDOM from "react-dom";
import {useState} from 'react'
function App() {const [num1, setNum1] = useState(0);const [num2, setNum2] = useState(0);function handleClick() {setNum1(c => c + 1);setNum2(c => c + 1);}console.log("render");return (<div>num1:{num1}-num2:{num2}<button onClick={handleClick}>Next</button></div>);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
点击一次按钮触发setNum1和setNum2,但是render只输出一次,React将多次更新合并处理,最终只渲染一次。
然而React18版本之前,批量更新并不是所有场景都会生效。
import * as ReactDOM from "react-dom";
import {useState} from 'react'
function App() {const [num1, setNum1] = useState(0);const [num2, setNum2] = useState(0);function handleClick() { setTimeout(()=>{setNum1(c => c + 1);setNum2(c => c + 1);})}console.log("render");return (<div>num1:{num1}-num2:{num2}<button onClick={handleClick}>Next</button></div>);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
点击一次按钮触发setNum1和setNum2,然后会输出两次render,这就是在React18版本之前,React的批量更新失效了。
总结:在React18之前,我们只能在React事件处理函数中执行过程中进行批量更新。对于promise、setTimeout、原生事件处理函数或其他任何事件中的状态更新都不会进行批量更新。
React18自动批量更新
从React18的createRoot开始,无论在哪里,所有更新都将自动进行批量更新。
这意味着 setTimeout、promises、原生事件处理函数或其他任何事件的批量更新都将与 React 事件一样,以相同的方式进行批量更新。我们希望这样可以减少渲染工作量,从而提高应用程序的性能:
// react 18.2.0 react-dom 18.2.0 react-scripts 5.0.1
import ReactDOM from 'react-dom/client';
import {useState} from 'react'
function App() {const [num1, setNum1] = useState(0);const [num2, setNum2] = useState(0);function handleClick() { setTimeout(()=>{setNum1(c => c + 1);setNum2(c => c + 1);})}console.log("render");return (<div>num1:{num1}-num2:{num2}<button onClick={handleClick}>Next</button></div>);
}
const root = ReactDOM.createRoot(document.getElementById('root')
);
root.render(<App />
);
点击一次按钮触发setNum1和setNum2,然后会输出一次render,这就是在React18版本,自动批量更新,所有更新都将进行批量更新。
禁止批量更新
使用flushSync来包裹更新,做到禁止批量更新
import ReactDOM from 'react-dom/client';
import {flushSync} from 'react-dom';
import {useState} from 'react';
function App() {const [num1, setNum1] = useState(0);const [num2, setNum2] = useState(0);function handleClick() { setTimeout(()=>{flushSync(()=>{setNum1(c => c + 1);})flushSync(()=>{setNum2(c => c + 1);})})}console.log("render");return (<div>num1:{num1}-num2:{num2}<button onClick={handleClick}>Next</button></div>);
}
const root = ReactDOM.createRoot(document.getElementById('root')
);
root.render(<App />
);
React18的自动批量更新对Hooks的影响
如果你正在使用 Hooks,在绝大多数情况下批量更新都能“正常工作”。
React18的自动批量更新对Classes的影响
如果在React17版本之前,没有在React事件处理函数中执行的代码不会批量执行,同时它也是同步执行,而在React18版本中,都是批量更新和异步执行的。所以会导致React18版本和React18版本之前的输出不一样。
在React18版本之前
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
export default class App extends Component {constructor(props) {super(props);this.state = {num1: 1,num2: 1}}handleClick(){setTimeout(()=>{this.setState(({num1})=>({num1: num1+1}))console.log(this.state);this.setState(({num2})=>({num2: num2+1}))})}render() {console.log('render');return (<div>num1:{this.state.num1}-num2:{this.state.num2}<button onClick={()=>{this.handleClick()}}>Next</button></div>)}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
/*上述代码点击的时候会输出一下内容:render{num1: 2, num2: 1}render
*/
在React18版本
import React, { Component } from 'react'
import ReactDOM from 'react-dom/client';
export default class App extends Component {constructor(props) {super(props);this.state = {num1: 1,num2: 1}}handleClick(){setTimeout(()=>{this.setState(({num1})=>({num1: num1+1}))console.log(this.state);this.setState(({num2})=>({num2: num2+1}))})}render() {console.log('render');return (<div>num1:{this.state.num1}-num2:{this.state.num2}<button onClick={()=>{this.handleClick()}}>Next</button></div>)}
}
const root = ReactDOM.createRoot(document.getElementById('root')
);
root.render(<App />
);
/*上述代码点击的时候会输出一下内容:{num1: 1, num2: 1}render
*/
如果想要在React18中按照React18版本之前的输出需要使用flushSync来进行修改
import React, { Component } from 'react'
import { flushSync } from 'react-dom';
import ReactDOM from 'react-dom/client';
export default class App extends Component {constructor(props) {super(props);this.state = {num1: 1,num2: 1}}handleClick(){setTimeout(()=>{flushSync(()=>{this.setState(({num1})=>({num1: num1+1}))})console.log(this.state);this.setState(({num2})=>({num2: num2+1}))})}render() {console.log('render');return (<div>num1:{this.state.num1}-num2:{this.state.num2}<button onClick={()=>{this.handleClick()}}>Next</button></div>)}
}
const root = ReactDOM.createRoot(document.getElementById('root')
);
root.render(<App />
);
/*上述代码点击的时候会输出一下内容:{num1: 1, num2: 1}render
*/
useState是同步还是异步
先上结论:
- 在React18版本之前,useState在React合成事件和hooks中是异步的,其他情况都是同步的,例如,原生事件、setTimeout、promise等
- 在React18版本,useState在React中都是异步的
React18版本之前的this.setState
import * as ReactDOM from "react-dom";
import React,{useState,useEffect} from 'react'
class App extends React.Component {state = {count: 0}componentDidMount() {this.setState({count: this.state.count + 1})console.log(this.state.count);this.setState({count: this.state.count + 1})console.log(this.state.count);setTimeout(() => {this.setState({count: this.state.count + 1})console.log(this.state.count);this.setState({count: this.state.count + 1})console.log(this.state.count);});}render() {return <h1>Count: {this.state.count}</h1>}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
上述代码输出0 0 2 3,我们来分析一下为什么这样输出。
- 首先进入
componentDidMount,因为this.setState是异步执行的,所以先执行同步代码,执行两次console.log(this.state.count);,输出两次0 - 在
componentDidMount中,this.setState导致的更新会进行合并,批量更新,只会更新最后一个,所以当还没执行setTimeout之前,更新count,这时的count变成了1 - 进入
setTimeout执行,在setTimeout中,this.setState是同步执行的,并且没有批量更新 - 执行
this.setState({count: this.state.count + 1}),这时count同步更新为2,执行console.log(this.state.count);输出2 - 执行
this.setState({count: this.state.count + 1}),这时count同步更新为3,执行console.log(this.state.count);输出4
所以最终输出0 0 2 3。
React18版本之前的this.setState怎么在setTimeout实现异步批量更新(unstable_batchedUpdates)
this.setState怎么在setTimeout实现异步批量更新,React提供了一种解决方案,就是从react-dom中暴露一个API:unstable_batchedUpdates,我们看一下具体的用法。
import * as ReactDOM from "react-dom";
import React,{useState,useEffect} from 'react'
class App extends React.Component {state = {count: 0}componentDidMount() {this.setState({count: this.state.count + 1})console.log(this.state.count);this.setState({count: this.state.count + 1})console.log(this.state.count)setTimeout(() => {ReactDOM.unstable_batchedUpdates(() => {this.setState({count: this.state.count + 1})console.log(this.state.count)this.setState({count: this.state.count + 1})console.log(this.state.count)}) })}render() {return <h1>Count: {this.state.count}</h1>}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
上述代码输出0 0 1 1,我们来分析一下为什么这样输出。
- 首先进入
componentDidMount,因为this.setState是异步执行的,所以先执行同步代码,执行两次console.log(this.state.count);,输出两次0 - 在
componentDidMount中,this.setState导致的更新会进行合并,批量更新,只会更新最后一个,所以当还没执行setTimeout之前,更新count,这时的count变成了1 - 进入
setTimeout执行,因为setTimeout中的代码用了unstable_batchedUpdates嵌套,所以也是异步批量更新。 - 先执行同步代码,执行两次
console.log(this.state.count),输出两次1。 - 执行
this.setState({count: this.state.count + 1}),此时count更新为2。 - 执行
this.setState({count: this.state.count + 1}),此时count更新为3。
React18的解决方案
如果在React17版本中,想要在setTimeout中执行异步批量更新,需要在每个setTimeout中都嵌套unstable_batchedUpdates,React18中解决了这个痛点,无论在哪里的更新都是异步批量更新。
import ReactDOM from 'react-dom/client';
import {flushSync} from 'react-dom';
import React from 'react';
class App extends React.Component {state = {count: 0}componentDidMount() {this.setState({count: this.state.count + 1})console.log(this.state.count);this.setState({count: this.state.count + 1})console.log(this.state.count);setTimeout(() => {this.setState({count: this.state.count + 1})console.log(this.state.count);this.setState({count: this.state.count + 1})console.log(this.state.count);});}render() {return <h1>React18 Count: {this.state.count}</h1>}
}
const root = ReactDOM.createRoot(document.getElementById('root')
);
root.render(<App />
);
上述代码输出0 0 1 1,我们来分析一下为什么这样输出。
- 首先进入
componentDidMount,因为this.setState是异步执行的,所以先执行同步代码,执行两次console.log(this.state.count);,输出两次0 - 在
componentDidMount中,this.setState导致的更新会进行合并,批量更新,只会更新最后一个,所以当还没执行setTimeout之前,更新count,这时的count变成了1 - 进入
setTimeout执行,在React18中,setTimeout中是异步批量更新。 - 先执行同步代码,执行两次
console.log(this.state.count),输出两次1。 - 执行
this.setState({count: this.state.count + 1}),此时count更新为2。 - 执行
this.setState({count: this.state.count + 1}),此时count更新为3。
相关文章:
第六十周总结——React数据管理
React数据管理 代码仓库 React批量更新 React中的批量更新就是将多次更新合并处理,最终只渲染一次,来获得更好的性能。 React18版本之前的批量更新 // react 17 react-dom 17 react-scripts 4.0.3 import * as ReactDOM from "react-dom"…...
Springboot之@Async异步指定自定义线程池使用
开发中会碰到一些耗时较长或者不需要立即得到执行结果的逻辑,比如消息推送、商品同步等都可以使用异步方法,这时我们可以用到Async。但是直接使用 Async 会有风险,当我们没有指定线程池时,他会默认使用其Spring自带的 SimpleAsync…...
视频知识点(23)- TS格式详解指南
*《音视频开发》系列-总览*(点我) 一、格式简介 TS视频封装格式,是一种被广泛应用的多媒体文件格式。它的全称是MPEG2-TS,其中TS是“Transport Stream”的缩写。TS(Transport Stream)流是一种传输流,它由固定长度(188 字节)的 TS 包组成,TS 包是对PES包的一种封装方式…...
linux篇【16】:传输层协议<后序>
目录 六.滑动窗口 (1)发送缓冲区结构 (2)滑动窗口介绍 (3)滑动窗口不一定只会向右移动。滑动窗口可以变大也可以变小。 (4)那么如果出现了丢包, 如何进行重传? 这里分两种情况…...
【C语言】动态内存管理
一.为什么存在动态内存分配? 我们已经掌握的内存开辟方式有:int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间 但是上述的开辟空间的方式有两个特点: 1. 空间开辟大小是固定的。 2. 数组在申明的…...
【Pytorch】AutoGrad个人理解
前提知识:[Pytorch] 前向传播和反向传播示例_友人小A的博客-CSDN博客 目录 简介 叶子节点 Tensor AutoGrad Functions 简介 torch.autograd是PyTorch的自动微分引擎(自动求导),为神经网络训练提供动力。torch.autograd需要对…...
华硕z790让独显和集显同时工作
系统用了一段时间,现在想让显卡主要做深度学习训练,集显用来连接显示器。却发现显示器接到集显接口无信号。 打售后客服也没有解决,现在把解决方案记录一下。 这是客服给的方案: 请开机后进BIOS---Advanced---System Agent (SA)…...
提高编程思维的python代码
1.通过函数取差。举例:返回有差别的列表元素 from math import floordef difference_by(a,b,fn):b set(map(fn, b))return [i for i in a if fn(i) not in b] print(difference_by([2.1, 1.2], [2.3, 3.4], floor))2.一行代码调用多个函数 def add(a, b):return …...
CSS背景background属性整理
1.background-color background-color属性:设置元素的背景颜色 2.background-position background-position属性:设置背景图像的起始位置,需要把 background-attachment 属性设置为 "fixed",才能保证该属性在 Firefo…...
AQS底层源码深度剖析-Lock锁
目录 AQS底层源码深度剖析-Lock锁 ReentrantLock底层原理 为什么把获取锁失败的线程加入到阻塞队列中,而不是采取其它方法? 总结:三大核心原理 CAS是啥? 代码模拟一个CAS: 公平锁与非公平锁 可重入锁的应用场景&…...
网络编程(二)
6. TCP 三次握手四次挥手 HTTP 协议是 Hype Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器(sever)传输超文本到客户端(本地浏览器…...
访问学者进入美国哪些东西不能带?
随着疫情的稳定,各国签证的逐步放开,成功申请到国外访问学者、博士后如何顺利的进入国外,哪些东西不能带,下面就随知识人网小编一起看一看。一、畜禽肉类(Meats, Livestock and Poultry)不论是新鲜的、干燥的、罐头的、真空包装的…...
灵巧手抓持<分类><仿真>
获取灵巧手抓取物体时的抓持类型,需要考虑:手本身的结构、被抓取物体的形状尺寸、抓持操作任务的条件。 研究方法:基于模型的方法、基于数据驱动的方法 基于模型的方法:建立灵巧手抓持相关的运动学和动力学模型建立目标函数求解…...
CENTO OS上的网络安全工具(十九)ClickHouse集群部署
一、VMware上集群部署ClickHouse (一)网络设置 1. 通过修改文件设置网络参数 (1)CentOS 在CENTOS上的网络安全工具(十六)容器特色的Linux操作_lhyzws的博客-CSDN博客中我们提到过可以使用更改配置文件的方式…...
tesseract -图像识别
下载链接:https://digi.bib.uni-mannheim.de/tesseract/如下选择最新的版本,这里我选择tesseract-ocr-w64-setup-5.3.0.20221222.exe有如下python模块操作tesseractpyocr 国内源:pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ py…...
JavaScript Math 算数对象
文章目录JavaScript Math 算数对象Math 对象Math 对象属性Math 对象方法算数值算数方法JavaScript Math 算数对象 Math(算数)对象的作用是:执行常见的算数任务。 Math 对象 Math(算数)对象的作用是:执行普…...
一体机HDATA节点添加和删除
瀚高数据库 目录 文档用途 详细信息 文档用途 一体机可在线添加、删除数据库集群节点。 详细信息 一体机可在线添加、删除数据库集群节点。具体操作步骤如下 一、节点添加 集群可以在其他机器上通过配置hghac.yaml文件,将新节点加入集群。 集群操作 1…...
关于 interface{} 会有啥注意事项?上
学习 golang ,对于 interface{} 接口类型,我们一定绕不过,咱们一起来看看 使用 interface{} 的时候,都有哪些注意事项吧 interface {} 可以用于模拟多态 xdm 咱们写一个简单的例子,就举动物的例子 写一个 Animal 的…...
Matlab中旧版modem.qammod与新版不兼容
最近,因为课题需要,在研究通信。在网上下了一个2015年左右的代码,其中用的是matlab旧版中的modem.qammod函数,但是旧版中的函数已经被删除了,(这里必须得吐槽一下,直接该函数内部运行机制就行呀…...
通达信指标公式颜色代码的四种写法(COLOR/RGB)
通达信指标公式颜色代码有四种写法,分别为COLOR颜色的英文、COLOR十六进制、RGBX十六进制、RGB(R,G,B)。标题有点尴尬,让我想到孔乙己“茴”字的四种写法,哈哈。 一、COLOR颜色的英文 “COLOR颜色的英文”这种写法比较简单,函数库…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
