react17+18 中 setState是同步还是异步更新

在类组件中使用setState,在函数式组件中使用hooks的useState。

setstate目录

  • 1. 类组件
    • 1.1 react 17版本
    • 1.2 react 18版本
  • 2、函数式组件


1. 类组件

1.1 react 17版本

参考内容:第十一篇:setState 到底是同步的,还是异步的?
                  彻底搞懂setState到底是同步还是异步(一)

1、在react可调度范围内的setState是异步的,并且多次setState会合并只执行最后一次,进行批量更新。

  • react合成事件
  • 生命周期
  • 事件处理,如onClick、onChange等。

2、在react可调度范围外的setState是同步的。

  • 宏任务 setTimeout、setInterval
  • 微任务 .then
  • 原生 js 绑定事件 document.addEventListener()

3、setState 并非真正的异步,而是通过设置全局变量 isBatchingUpdates 来判断 setState是先存进队列还是直接更新,如果值为true,则执行异步操作,如果为false,则直接更新。

      isBatchingUpdates 会在 React 可以控制的地方,则为true,比如React生命周期和合成事件中,而在外部 react 无法控制的地方,比如原生事件,具体就是在 addEventListener、setTimeout、setInterval 、.then等事件中,就只能同步。

4、一般认为,做异步设计是为了性能优化,减少渲染次数。React团队的观点是:

  • 保持内部一致性。如果将 state 改为同步更新,那尽管 state 的更新是同步的,但是 props不是。
  • 启用并发更新,完成异步渲染。

5、案例

import React from "react";
export default class App extends React.Component{

  state = {
    count: 0
  }

  increment = () => {
    console.log('increment setState前的count', this.state.count)  //0
    this.setState({
      count: this.state.count + 1
    }); //异步
    console.log('increment setState后的count', this.state.count)   //0
  }  //最终count变为1

  triple = () => {
    console.log('triple setState前的count', this.state.count)   //1
    this.setState({
      count: this.state.count + 1
    });
    this.setState({
      count: this.state.count + 1
    });
    this.setState({
      count: this.state.count + 1
    });//异步,且合并
    console.log('triple setState后的count', this.state.count)   //1
  }   //最终count变为 2

  reduce = () => {
    setTimeout(() => {
      console.log('reduce setState前的count', this.state.count)  //2
      this.setState({
        count: this.state.count - 1
      });//同步  count变为 1
      console.log('reduce setState后的count', this.state.count)   //1
    },0);
  }

  render(){
    return <div>
      <button onClick={this.increment}>点我增加</button>
      <button onClick={this.triple}>点我增加三倍</button>
      <button onClick={this.reduce}>点我减少</button>
    </div>
  }
}

接着我把组件挂载到 DOM 上:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

const rootElement = document.getElementById("root");

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  rootElement
);

此时浏览器里渲染出来的是如下图所示的三个按钮:
在这里插入图片描述此时有个问题,若从左到右依次点击每个按钮,控制台的输出会是什么样的?

在这里插入图片描述

1.2 react 18版本

参考内容:彻底搞懂setState到底是同步还是异步(二)

      React18版本引入了自动批处理功能,批处理是指,当 React 在一个单独的重渲染事件中批量处理多个状态更新以此实现优化性能。如果没有自动批处理的话,我们仅能够在 React 事件处理程序中批量更新。在 React 18 之前,默认情况下 promise、setTimeout、原生应用的事件处理程序以及任何其他事件中的更新都不会被批量处理;但现在,这些更新内容都会被自动批处理。

1、在react18中setTimeout里的setState也是异步的批量处理了,都是setTimeout 中的批处理明显落后外部的批处理
     上面代码在react18中的结果:0 0 1 1 2 2.

2、案例

import React from 'react';
import './App.css';

class AppClass extends React.Component {
  state = {
    count: 0,
  };

  handleClick = () => {
    this.setState({ count: 1 });
    console.log('count: ', this.state.count);   //0

    this.setState({ count: 2 });
    console.log('count: ', this.state.count);   //0

	//异步,合并,count的值变为2

    setTimeout(() => {
      this.setState({ count: 3 });
      console.log('count: ', this.state.count);  //2

      this.setState({ count: 4 });
      console.log('count: ', this.state.count);   //2
    }, 0);
	//异步,合并,count的值变为4
  };

  render() {
    return (
      <div className='App'>
        <button onClick={this.handleClick}>count = {this.state.count}</button>
      </div>
    );
  }
}

export default AppClass;

react 18的控制台打印结果:
在这里插入图片描述
react 17的打印结果:

在这里插入图片描述
对比下结果:

  • 前两次的结果相同,都是0,证明这块是跟 v17 中一样的,都是异步
  • 后两次结果不一样,v17中是同步更新的,所以每次setState 后都可以立即获取到更新后的值,但v18 中打印的是两个2 ,说明是异步更新的,只是这个异步更新跟setTimeout 外部的不在一个批中,setTimeout 中的批处理明显落后外部的批处理。

页面会渲染几次? (执行setState会重新渲染页面)

  • react17中在setTimeout外会合并渲染一次,在setTimeout中是同步的,会渲染两次,总共页面会渲染三次。
  • react18中在setTimeout外会合并渲染一次,在setTimeout中式异步的,进行自动批处理,会渲染一次,总共页面会渲染两次。

2、函数式组件

参考内容:彻底搞懂setState到底是同步还是异步(三)

函数式组件中使用hooks的useState。

1、react 17:由于闭包输出的内容全部是count初始值。在setTimeout外部是异步批处理,在setTimeout内部不是批处理,与react 17中的类组件是否批处理一样。

2、react 18:由于闭包输出的内容全部是count初始值。在setTimeout外部是异步批处理,在setTimeout内部也是自动批处理,与react 18中的类组件是否批处理一样。

3、案例
把上述题目改造成hooks的形式:

import { useState } from 'react';
import './App.css';

function App() {
  const [count, setCount] = useState(0);

  const handle = () => {
    setCount(1);
    console.log('count: ', count);  //0

    setCount(2);
    console.log('count: ', count);   //0
	
    setTimeout(() => {
      setCount(3);
      console.log('count: ', count);   //0

      setCount(4);
      console.log('count: ', count);   //0
    }, 0);

  };

  return (
    <>
      <div className='App'>
        <button onClick={handle}>count is {count}</button>
      </div>
    </>
  );
}

export default App;

react 17的执行结果:

在这里插入图片描述
react 18的执行结果:

在这里插入图片描述

输出结果都是0,看似都是在批处理,为了能更清楚的看到 React 的渲染行为,修改下上边的代码,在每次渲染都都打印下当前的 count值,添加如下代码:

useEffect(() => {
    console.log('render: 此时的count: ', count);
  });

再次查看输出结果:

在这里插入图片描述
在这里插入图片描述

分析下:

  • 在 React 17 下,render 打印了 3 次,说明组件重新渲染了 3 次,回顾下当时讲解 React 17 的 setState时,setTimeout外是批处理重新渲染一次,setTimeout中是同步渲染,重新渲染两次,能对上。
  • 再来看下 React 18 下,render 打印了 2 次,说明组件重新渲染了 2 次,在对比下前面讲解的 React18 的自动批处理功能,setTimeout外部批处理一次,内部批处理一次,重新渲染两次,也能对上.
  • 那为什么这期间打印的 count 都是 0 呢?

        这是由于js的特性闭包导致的,闭包就是内部函数可以访问外部函数的变量。

        组件App是一个函数,handle 是 App内的函数,这也就形成了一个闭包,所以 handle 函数才能访问到 count 变量的值。

        无论延时多长时间,最终打印的count值一直是旧值,也就是 0。

        要是想要立即用更新后的state,可以使用setState的第二个函数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity类银河恶魔城学习记录12-8 p130 Skill Tree UI源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI.cs using UnityEngine;public class UI : MonoBehaviour {[SerializeFi…

【精选】发布应用到应用商店的基本介

摘要 本文旨在介绍如何在各大应用商店发布应用&#xff0c;包括市场选择、准备材料、上架步骤以及常见被拒原因及解决方法。通过详细的步骤和经验分享&#xff0c;帮助开发者顺利将应用推向市场。 引言 随着移动应用市场的不断发展&#xff0c;越来越多的开发者希望将他们的…

C++类和对象上

C和C语言本质区别 C语言是面向过程的&#xff0c;面向过程的&#xff0c;分析出求解问题的步骤&#xff0c;然后逐步通过函数调用来逐步解决问题。 C在分析问题是在面对对象的基础上来实现的&#xff0c;即将一件事情拆分为不同的对象&#xff0c;靠的是对象之间的交互来完成的…

OSPF数据报文格式

OSPF协议是跨层封装的协议&#xff0c;跨四层封装&#xff0c;直接将应用层的数据封装在网络层协议后面&#xff0c;IP协议包中协议号字段对应的数值为——89 OSPF的头部信息&#xff1a; ——所有数据包公有的信息 版本&#xff1a;OSPF版本 在IPV4中一般使用OSPFV2&#xf…

c 解数独(通用方法,适用于9×9 数独)

折腾了一周时间&#xff0c;终于搞定99数独通用方法 思路&#xff1a;1.生成每行空位的值&#xff0c;也就是1-9中除去非0的数。 2.用行&#xff0c;列&#xff0c;宫判断每行中每个空位的最小取值范围后再重新生成每行。 3.随机提取生成的9行&#xff0c;判断每列之和是否等…

找不到vcruntime140.dll怎么办,vcruntime140.dll丢失的多种解决方法

在我们日常频繁地与电脑打交道、依赖其处理各种工作、学习乃至娱乐任务的过程中&#xff0c;偶尔会遭遇一些令人困扰的技术问题。其中一种颇为常见的情况便是&#xff0c;当您正全神贯注于某个重要应用的操作&#xff0c;或是满怀期待地试图启动一款新安装的游戏时&#xff0c;…

2万亿训练数据!Stable LM 2-12B加入开源队列

公*众*号&#xff1a;AI疯人院 4月9日&#xff0c;知名大型模型开源平台Stability.ai在其官网上发布了全新的类ChatGPT模型——Stable LM 2 12B。 据了解&#xff0c;Stable LM 2 12B模型拥有120亿个参数&#xff0c;其训练数据涵盖了英语、西班牙语、德语等7种语言的2万亿个…

C++修炼之路之string--标准库中的string

目录 前言 一&#xff1a;标准库的string类简介 1.string是basic_string的一份char类型的类模板 2.basic_string类模板的分类 3.string是表示字符串的字符串类 4.在使用string类时要添加头文件#include 二&#xff1a;string类的常用接口(只介绍常用的) 1.构造析构赋…

今日arXiv最热大模型论文:Dataverse,针对大模型的开源ETL工具,数据清洗不再难!

引言&#xff1a;大数据时代下的ETL挑战 随着大数据时代的到来&#xff0c;数据处理的规模和复杂性不断增加&#xff0c;尤其是在大语言模型&#xff08;LLMs&#xff09;的开发中&#xff0c;对海量数据的需求呈指数级增长。这种所谓的“规模化法则”表明&#xff0c;LLM的性…

ETLCloud结合kafka的数据集成

一、ETLCloud中实时数据集成的使用 在ETLCloud中数据集成有两种方式&#xff0c;一种是离线数据集成&#xff0c;另一种便是我们今天所要介绍的实时数据集成了&#xff0c;两者的区别从名字便可以得知&#xff0c;前者处理的数据是离线的没有时效性的&#xff0c;后者的数据是…

常见的解析漏洞总结

文件解析漏洞 文件解析漏洞主要由于网站管理员操作不当或者 Web 服务器自身的漏洞&#xff0c;导致一些特殊文件被 IIS、apache、nginx 或其他 Web服务器在某种情况下解释成脚本文件执行。 比如网站管理员配置不当&#xff0c;导致php2、phtml、ascx等等这些文件也被当成脚本文…

【VScode】同时编辑多处

【VScode】同时编辑多处 1. 多光标自定义批量编辑2. 选择多个&#xff0c;同时操作(批量选中局部匹配项)3. 取消选择4. 在不移动光标的情况下滚动屏幕5. 批量选中全局匹配项6.重点6.1 通过上下键选择多行6.2 同时选中所有行的末尾6.3 选中多列另一种方式6.4 通过正则的方式配置…

显示学习4(基于树莓派Pico) -- 游戏

来自&#xff1a;https://github.com/zelacerda/micropython 代码改造了一下&#xff0c;让它可以跑起来。 简单分析一下代码。外层是一个死循环&#xff0c;有一个状态机来对应不同的场景。 def loop():while True:if state 0: splash_screen()elif state 1: game_waiti…

《数学大世界》期刊点评_栏目设置_投稿指南

《数学大世界》期刊点评_栏目设置_投稿指南 《数学大世界》知网 5000字符3版 收录小中高数学 教研类文章 理论&#xff0b;课题实例 23.1-7月版面&#xff1b; 24年3-4月版面也可安排 主管单位&#xff1a;吉林出版集团股份有限公司 主办单位&#xff1a;北方妇女儿童出版…

Python-VBA函数之旅-bytearray函数

目录 1、bytearray函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、相关文章&#xff1a; 个人主页&#xff1a;非风V非雨-CSDN博客 bytearray函数在Python中提供了一种可变字节序列的表示方式&#xff0c;这在实际编程中有多种应用场景。常见的应用场…

基于springboot+vue+Mysql的职称评审管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Web 前端性能优化之八:前端性能检测实践

五、前端性能检测实践 1、常用的检测工具 Lighthouse、Chrome开发者工具中与性能检测相关的一些工具面板、页面加载性能分析工具PageSpeed Insights、专业的性能检测工具WEBPAGETEST等 1、Chrome 任务管理器 通过Chrome任务管理器我们可以查看当前Chrome浏览器中&#xff0…

机器学习-08-关联规则和协同过滤

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中关联规则和协同过滤。 参考 机器学习&#xff08;三&#xff09;&#xff1a;Apriori算法&#xff08;算法精讲&#xff09; Apriori 算法 理论 重点 MovieLens:一个常用的电影推荐系统领域的数据集 2…

苍穹外卖---文件上传-阿里OSS

一&#xff1a;开通阿里云对象存储服务oss,创建bucket&#xff0c;获得密钥 二&#xff1a;在程序中集成上传文件功能 1.连接阿里云OSS对象存储服务器 声明一个配置属性的文件用于传入连接的参数 package com.sky.properties;import lombok.Data; import org.springframewo…

three.js跟着教程实现VR效果(四)

参照教程&#xff1a;https://juejin.cn/post/6973865268426571784&#xff08;作者&#xff1a;大帅老猿&#xff09; 1.WebGD3D引擎 用three.js &#xff08;1&#xff09;使用立方体6面图 camera放到 立方体的中间 like “回” 让贴图向内翻转 &#xff08;2&#xff09;使…