当前位置: 首页 > news >正文

第六十周总结——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是同步还是异步

先上结论:

  1. 在React18版本之前,useState在React合成事件和hooks中是异步的,其他情况都是同步的,例如,原生事件、setTimeout、promise等
  2. 在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,我们来分析一下为什么这样输出。

  1. 首先进入componentDidMount,因为this.setState是异步执行的,所以先执行同步代码,执行两次console.log(this.state.count);,输出两次0
  2. componentDidMount中,this.setState导致的更新会进行合并,批量更新,只会更新最后一个,所以当还没执行setTimeout之前,更新count,这时的count变成了1
  3. 进入setTimeout执行,在setTimeout中,this.setState是同步执行的,并且没有批量更新
  4. 执行this.setState({count: this.state.count + 1}),这时count同步更新为2,执行console.log(this.state.count);输出2
  5. 执行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,我们来分析一下为什么这样输出。

  1. 首先进入componentDidMount,因为this.setState是异步执行的,所以先执行同步代码,执行两次console.log(this.state.count);,输出两次0
  2. componentDidMount中,this.setState导致的更新会进行合并,批量更新,只会更新最后一个,所以当还没执行setTimeout之前,更新count,这时的count变成了1
  3. 进入setTimeout执行,因为setTimeout中的代码用了unstable_batchedUpdates嵌套,所以也是异步批量更新。
  4. 先执行同步代码,执行两次console.log(this.state.count),输出两次1。
  5. 执行this.setState({count: this.state.count + 1}),此时count更新为2。
  6. 执行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,我们来分析一下为什么这样输出。

  1. 首先进入componentDidMount,因为this.setState是异步执行的,所以先执行同步代码,执行两次console.log(this.state.count);,输出两次0
  2. componentDidMount中,this.setState导致的更新会进行合并,批量更新,只会更新最后一个,所以当还没执行setTimeout之前,更新count,这时的count变成了1
  3. 进入setTimeout执行,在React18中,setTimeout中是异步批量更新。
  4. 先执行同步代码,执行两次console.log(this.state.count),输出两次1。
  5. 执行this.setState({count: this.state.count + 1}),此时count更新为2。
  6. 执行this.setState({count: this.state.count + 1}),此时count更新为3。

相关文章:

第六十周总结——React数据管理

React数据管理 代码仓库 React批量更新 React中的批量更新就是将多次更新合并处理&#xff0c;最终只渲染一次&#xff0c;来获得更好的性能。 React18版本之前的批量更新 // react 17 react-dom 17 react-scripts 4.0.3 import * as ReactDOM from "react-dom"…...

Springboot之@Async异步指定自定义线程池使用

开发中会碰到一些耗时较长或者不需要立即得到执行结果的逻辑&#xff0c;比如消息推送、商品同步等都可以使用异步方法&#xff0c;这时我们可以用到Async。但是直接使用 Async 会有风险&#xff0c;当我们没有指定线程池时&#xff0c;他会默认使用其Spring自带的 SimpleAsync…...

视频知识点(23)- TS格式详解指南

*《音视频开发》系列-总览*(点我) 一、格式简介 TS视频封装格式,是一种被广泛应用的多媒体文件格式。它的全称是MPEG2-TS,其中TS是“Transport Stream”的缩写。TS(Transport Stream)流是一种传输流,它由固定长度(188 字节)的 TS 包组成,TS 包是对PES包的一种封装方式…...

linux篇【16】:传输层协议<后序>

目录 六.滑动窗口 &#xff08;1&#xff09;发送缓冲区结构 &#xff08;2&#xff09;滑动窗口介绍 &#xff08;3&#xff09;滑动窗口不一定只会向右移动。滑动窗口可以变大也可以变小。 &#xff08;4&#xff09;那么如果出现了丢包, 如何进行重传? 这里分两种情况…...

【C语言】动态内存管理

一.为什么存在动态内存分配&#xff1f; 我们已经掌握的内存开辟方式有&#xff1a;int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间 但是上述的开辟空间的方式有两个特点&#xff1a; 1. 空间开辟大小是固定的。 2. 数组在申明的…...

【Pytorch】AutoGrad个人理解

前提知识&#xff1a;[Pytorch] 前向传播和反向传播示例_友人小A的博客-CSDN博客 目录 简介 叶子节点 Tensor AutoGrad Functions 简介 torch.autograd是PyTorch的自动微分引擎&#xff08;自动求导&#xff09;&#xff0c;为神经网络训练提供动力。torch.autograd需要对…...

华硕z790让独显和集显同时工作

系统用了一段时间&#xff0c;现在想让显卡主要做深度学习训练&#xff0c;集显用来连接显示器。却发现显示器接到集显接口无信号。 打售后客服也没有解决&#xff0c;现在把解决方案记录一下。 这是客服给的方案&#xff1a; 请开机后进BIOS---Advanced---System Agent (SA)…...

提高编程思维的python代码

1.通过函数取差。举例&#xff1a;返回有差别的列表元素 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属性&#xff1a;设置元素的背景颜色 2.background-position background-position属性&#xff1a;设置背景图像的起始位置&#xff0c;需要把 background-attachment 属性设置为 "fixed"&#xff0c;才能保证该属性在 Firefo…...

AQS底层源码深度剖析-Lock锁

目录 AQS底层源码深度剖析-Lock锁 ReentrantLock底层原理 为什么把获取锁失败的线程加入到阻塞队列中&#xff0c;而不是采取其它方法&#xff1f; 总结&#xff1a;三大核心原理 CAS是啥&#xff1f; 代码模拟一个CAS&#xff1a; 公平锁与非公平锁 可重入锁的应用场景&…...

网络编程(二)

6. TCP 三次握手四次挥手 HTTP 协议是 Hype Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写&#xff0c;是用于从万维网&#xff08;WWW&#xff1a;World Wide Web&#xff09;服务器&#xff08;sever&#xff09;传输超文本到客户端&#xff08;本地浏览器…...

访问学者进入美国哪些东西不能带?

随着疫情的稳定&#xff0c;各国签证的逐步放开&#xff0c;成功申请到国外访问学者、博士后如何顺利的进入国外&#xff0c;哪些东西不能带&#xff0c;下面就随知识人网小编一起看一看。一、畜禽肉类(Meats, Livestock and Poultry)不论是新鲜的、干燥的、罐头的、真空包装的…...

灵巧手抓持<分类><仿真>

获取灵巧手抓取物体时的抓持类型&#xff0c;需要考虑&#xff1a;手本身的结构、被抓取物体的形状尺寸、抓持操作任务的条件。 研究方法&#xff1a;基于模型的方法、基于数据驱动的方法 基于模型的方法&#xff1a;建立灵巧手抓持相关的运动学和动力学模型建立目标函数求解…...

CENTO OS上的网络安全工具(十九)ClickHouse集群部署

一、VMware上集群部署ClickHouse &#xff08;一&#xff09;网络设置 1. 通过修改文件设置网络参数 &#xff08;1&#xff09;CentOS 在CENTOS上的网络安全工具&#xff08;十六&#xff09;容器特色的Linux操作_lhyzws的博客-CSDN博客中我们提到过可以使用更改配置文件的方式…...

tesseract -图像识别

下载链接&#xff1a;https://digi.bib.uni-mannheim.de/tesseract/如下选择最新的版本&#xff0c;这里我选择tesseract-ocr-w64-setup-5.3.0.20221222.exe有如下python模块操作tesseractpyocr 国内源&#xff1a;pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ py…...

JavaScript Math 算数对象

文章目录JavaScript Math 算数对象Math 对象Math 对象属性Math 对象方法算数值算数方法JavaScript Math 算数对象 Math&#xff08;算数&#xff09;对象的作用是&#xff1a;执行常见的算数任务。 Math 对象 Math&#xff08;算数&#xff09;对象的作用是&#xff1a;执行普…...

一体机HDATA节点添加和删除

瀚高数据库 目录 文档用途 详细信息 文档用途 一体机可在线添加、删除数据库集群节点。 详细信息 一体机可在线添加、删除数据库集群节点。具体操作步骤如下 一、节点添加 集群可以在其他机器上通过配置hghac.yaml文件&#xff0c;将新节点加入集群。 集群操作 1&#xf…...

关于 interface{} 会有啥注意事项?上

学习 golang &#xff0c;对于 interface{} 接口类型&#xff0c;我们一定绕不过&#xff0c;咱们一起来看看 使用 interface{} 的时候&#xff0c;都有哪些注意事项吧 interface {} 可以用于模拟多态 xdm 咱们写一个简单的例子&#xff0c;就举动物的例子 写一个 Animal 的…...

Matlab中旧版modem.qammod与新版不兼容

最近&#xff0c;因为课题需要&#xff0c;在研究通信。在网上下了一个2015年左右的代码&#xff0c;其中用的是matlab旧版中的modem.qammod函数&#xff0c;但是旧版中的函数已经被删除了&#xff0c;&#xff08;这里必须得吐槽一下&#xff0c;直接该函数内部运行机制就行呀…...

通达信指标公式颜色代码的四种写法(COLOR/RGB)

通达信指标公式颜色代码有四种写法&#xff0c;分别为COLOR颜色的英文、COLOR十六进制、RGBX十六进制、RGB(R,G,B)。标题有点尴尬&#xff0c;让我想到孔乙己“茴”字的四种写法&#xff0c;哈哈。 一、COLOR颜色的英文 “COLOR颜色的英文”这种写法比较简单&#xff0c;函数库…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...