第六十周总结——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颜色的英文”这种写法比较简单,函数库…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
