react useState的初始化函数 初始化值为props时的同步问题 | setState函数的使用与异步更新

文章目录

  • react setState函数
    • useState()钩子创建state
      • 如何根据props更新state值
    • setState的参数是下一个状态state
    • setState的参数是更新函数function
    • setState异步与同步
      • 合成事件
      • setState 实现原理

react setState函数

useState()钩子创建state

const [state, setState] = useState(initialState)`

useState钩子函数向组件添加一个状态变量,该函数返回状态变量(只用于显示)与设置状态变量的setState函数。

useState的使用细节

  1. 调用修改函数修改state变量的值(state值发生变化)会触发组件的重新渲染,直接修改state变量不会触发组件的重新渲染。
  2. 组件重新渲染等同于函数重新执行,函数体的代码会再次调用。执行useState时会返回本次渲染的state值。setState()是由钩子函数useState()生成的,useState()会保证数组的每次渲染都会获取到相同的setState()
  3. React只在初次渲染时保存初始状态,后续渲染将其忽略。

参数的两种形式

  1. 初始化函数
    惰性初始化函数:如果useState的参数是一个生成数据的函数,则可以直接将这个函数本身传递(仅在初始化期间调用一次),不是函数()(这种情况每次渲染时调用此函数)
    如果函数需要传参,可以在useState之前使用useMemo缓存该函数的结果。

  2. 参数是值 - 如果是props需要注意同步的问题

如何根据props更新state值

为什么父组件传递的props改变,子组件state值不同步更新?

父组件传递给子组件的props发生改变,引发子组件的render,并没有执行子组件的constructor函数(子组件没有被卸载自然不会重新加载)只会重新render。父组件的props传递给子组件的state,子组件的state只会在第一次加载的时候被赋值,后续的父组件props变化并不会被赋值到子组件的state

解决办法

  1. 子组件使用useEffect()
    useEffect的执行时机组件挂载->state改变->DOM改变->绘制屏幕->useEffect,所以如果在useEffect里更新state的数据,会在屏幕绘制成功后又重新开始这个流程。

    尽量不使用,官方解释:https://zh-hans.react.dev/learn/you-might-not-need-an-effect

    // 方法1:子组件使用useEffect
    const [tags, setTags] = useState(() => props.tags);
      useEffect(() => {
    setTags(props.tags);
    }, [props.tags]);
    
  2. 父组件使用子组件时,绑定key。当父组件要传递的props改变时改变key值,将子组件认为是不同的子组件(引发子组件重新加载而不是单纯的渲染) –推荐

    <children userId={userId} key={userId}/>
    

setState的参数是下一个状态state

  1. 在 React 中,状态被认为是只读的,因此应该替换它而不是改变现有对象。
  2. state值是一个对象时,setState(state)修改的参数,由于是下一次渲染的state值(组件重渲染后useState返回state值),需要使用新的对象去替换已有对象
// user是对象,对象的地址没有发生变化,所以不会引起组件重新渲染
const [user, setUser] = useState({name:"ranran",age:18})
user.name = "xxx"; 
setUser(user);


// 解决方案:将其拷贝给另一个新对象,修改新对象的属性
setUser({...user,name:"xxx"}) // 后面的name会覆盖前面的name

setState()去修改一个state时,并不表示修改当前的state,修改的是组件下一次渲染的state(下一次 渲染中useState返回的内容)

说明案例1
假设name的初始值是Taylor,点击按钮后触发handleClick 函数,修改name的值,打印发现是修改前的值Taylor

// 点击按钮修改名字
const handleClick = ()=> {
  setName('Robin'); // 下一次渲染中useState返回的内容
  console.log(name); // Still "Taylor"! 本次渲染useState返回的内容
}

说明案例2
假设 age42,这个处理函数三次调用 setAge(age + 1),点击一次后,age 将只会变为 43

const handleClick() => {
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
}

setState的参数是更新函数function

更新函数的参数是待定状态state

const handleClick = () => {
  setAge(a => a + 1); // setAge(42 => 43)
  setAge(a => a + 1); // setAge(43 => 44)
  setAge(a => a + 1); // setAge(44 => 45)
}

React 将更新函数放入 队列 中。然后,在下一次渲染期间(组件重渲染,useState返回state的过程中),它将按照相同的顺序调用:
1.a => a + 1 将接收 42 作为待定状态,并返回 43 作为下一个状态。
2.a => a + 1 将接收 43 作为待定状态,并返回 44 作为下一个状态。
3.a => a + 1 将接收 44 作为待定状态,并返回 45 作为下一个状态。

按照惯例,通常将待定状态参数命名为状态变量名称的第一个字母,如 age 为 a。然而,你也可以把它命名为 prevAge 或者其他你觉得更清楚的名称。

批量更新
多个顺序的setState不是同步地一个一个执行,会一个一个加入队列,然后最后一起执行。在异步setState更新队列时,存储的是合并状态(上述案例age的最终状态45)。因此前面设置的 key 值会被后面所覆盖,最终只会执行一次更新。

setState异步与同步

明确这里所说的同步和异步指的是 API 调用后更新 DOM 是同步还是异步的。
react18之后已经全部异步了

设计为异步更新的原因

  1. setState设计为异步,可以显著的提升性能:如果每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的;最好的办法应该是获取到多个更新,之后进行批量更新(这也为什么在react可控范围类,希望setState是异步的)。
  2. 如果同步更新了state,但是还没有执行render函数,而且props依赖于state中的数据,那么stateprops不能保持同步;stateprops不能保持一致性,会在开发中产生很多的问题。

setState是同步还是异步呢?

  • 在React内部机制能检测到的地方,组件生命周期(除componentDidUpdate) 或React合成事件中,setState是异步。
  • 如果 setState 在原生 JavaScript 控制的范围被调用,setState是同步。

setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”。

合成事件

React 中为元素添加的事件被叫做合成事件(区分一下js的原生事件)

合成事件的好处

  • 屏蔽了浏览器之间关于事件处理的兼容性问题,为合成事件对象内部提供了统一的 API。
  • 提升性能, React 并不会将事件添加到真正的DOM 元素上,事件都被委托给 document

步骤说明
1.React 会在拥有事件的 DOM 对象身上添加一个 store 对象,在 store 对象中存储事件名称及事件处理函数,然后通过document 分发事件。
2.当事件被触发后,通过获取事件源对象,查看事件源对象中是否存在store对象,获取 store 对象中事件处理函数,执行事件处理函数。

核心实现
react 中所有的合成事件都会经过dispatchEventForLegacyPluginEventSystem()处理,其中通过设置全局变量isBatchingEventUpdates来标志当前的变化是否发生在React的可调度范围内。

/* 所有的事件都将经过此函数统一处理 */
function dispatchEventForLegacyPluginEventSystem(){
    // handleTopLevel 事件处理函数 batchedEventUpdates 批量更新
    batchedEventUpdates(handleTopLevel, bookKeeping);
}
function batchedEventUpdates(fn,a){
    /* 开启批量更新  */
    
   isBatchingEventUpdates = true;
  try {
    /* 这里执行了的事件处理函数, 比如在一次点击事件中触发setState,那么它将在这个函数内执行 */
    return batchedEventUpdatesImpl(fn, a, b);
  } finally {
    /* 完成一次事件,批量更新  */
    isBatchingEventUpdates = false;
  }
}

setState 实现原理

  1. 当 setState 方法被调用后,将状态传递给组件更新器,让组件更新器将状态临时存储起来。每个组件都会有自己的组件更新器,当需要更新组件时调用组件更新器。
  2. 状态临时保存完成后判断当前是否为批量更新模式,如果是,将组件更新器添加到更新队列中;如果不是,直接更新组件。
    当触发合成事件时, 在事件处理函数执行之前,会先将批量更新模式设置为 true,然后执行事件处理函数收集状态。当事件处理函数执行完成后,执行批量更新操作(从更新队列中获取组件更新器并调用)。组件更新器调用完成后再将批量更新模式设置为 false
  3. 更新组件时,先判断是否有状态需要更新,如果有就先计算最新状态,将得出的最新状态重新设置给组件(useState的返回值?)。
    计算状态时,如果状态是函数类型,调用函数传入当前状态,返回最新状态。如果状态是对象类型,使用对象状态覆盖原有状态。
  4. 组件状态计算完成后,通过调用组件内部的 render 方法获取新的 VirtualDOM,再通过 DOM 对象获取旧的虚拟 DOM,然后调用 diff 方法进行比对,对比完成后将差异更新到真实 DOM 对象中。

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

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

相关文章

探索数据结构:链式队与循环队列的模拟、实现与应用

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 队列的定义 队列&#xff08;queue&#xff09;是一种只允许在一端进…

原来这就是线程安全(一)

TOC 一:什么是线程不安全?? 先看一段代码: public class Demo1 {public static int count 0;public static void main(String[] args) throws InterruptedException {Thread t1new Thread(()->{for (int i 0; i < 50000; i) {count;}});Thread t2new Thread(()-&g…

Linux-进程控制

&#x1f30e;进程控制【上】 文章目录&#xff1a; 进程控制 为什么要有地址空间和页表 程序的内存       程序申请内存使用问题 写时拷贝与缺页中断 父子进程代码共享       为什么需要写时拷贝       页表的权限位       缺页中断 退出码和错误码…

P3369 【模板】普通平衡树(splay 算法)

题目描述 您需要写一种数据结构&#xff08;可参考题目标题&#xff09;&#xff0c;来维护一些数&#xff0c;其中需要提供以下操作&#xff1a; 插入一个数 x。删除一个数 x&#xff08;若有多个相同的数&#xff0c;应只删除一个&#xff09;。定义排名为比当前数小的数的…

Pytorch从零开始实战22

Pytorch从零开始实战——CycleGAN实战 本系列来源于365天深度学习训练营 原作者K同学 内容介绍 CycleGAN是一种无监督图像到图像转换模型&#xff0c;它的一个重要应用领域是域迁移&#xff0c;比如可以把一张普通的风景照变化成梵高化作&#xff0c;或者将游戏画面变化成真…

2024软件设计师备考讲义——UML(统一建模语言)

UML的概念 用例图的概念 包含 <<include>>扩展<<exted>>泛化 用例图&#xff08;也可称用例建模&#xff09;描述的是外部执行者&#xff08;Actor&#xff09;所理解的系统功能。用例图用于需求分析阶段&#xff0c;它的建立是系统开发者和用户反复…

4G/5G防爆布控球

#防爆布控球 #远程实时监控 #移动应急指挥 #高清图像采集 #防爆安全认证 4G/5G防爆布控球 M130-EX防爆布控球是针对石化装置、石油平台、燃气、化工、制药、煤炭、冶炼、船舶制造、纺织等易燃易爆环境及危险场所而开发设计的防爆智能一体化电气设备。 产品型号&#xff1a;M13…

Antd Vue3 使用 Anchor 锚点组件记录

项目场景 客户要求做一个表单页面&#xff0c;表单数据分为三步&#xff0c;每一步骤是一个单独的 Vue 组件&#xff0c;表单上方需要使用锚点组件实现锚点定位到每一步的功能。 代码总览 <template><div class"guided-form-content-wrapper"><!-- …

CKS之Kubernetes审计日志

目录 概述 审计事件阶段 审计日志级别 None Metadata Request RequestResponse 审计日志的使用 步骤1&#xff1a;配置审计策略文件 步骤2&#xff1a;配置API Server 步骤3&#xff1a;配置日志存储 注意事项 审计策略与规则 审计日志样例 使用场景 概述 Kube…

一、JAVA集成海康SDK

JAVA集成海康SDK 文章目录 JAVA集成海康SDK前言一、项目依赖 jar1. examples.jar2. 项目依赖 jna.jar,可以通过 maven依赖到。二、集成SDK1.HcNetSdkUtil 海康 SDK封装类2.HCNetSDK3.Linux系统集成SDK三、总结前言 提示:首先去海康官网下载 https://open.hikvision.com/dow…

stable diffusion如何下载模型?

文件夹里面有14个模型&#xff0c;把这些模型复制到SD文件夹里 具体位置:SD文件>models>ControlNet

【C/C++】从零开始认识C++历程-启航篇

文章目录 &#x1f4dd;前言&#x1f320; 什么是C&#xff1f;&#x1f309;C的发展史 &#x1f320;C的重要性&#x1f309;语言的使用广泛度 &#x1f320;在工作领域&#x1f309; 岗位需求 &#x1f320;相关笔试题&#x1f309; 公司怎样面试C &#x1f6a9;总结 &#x…

蓝桥杯 - 小明的背包1(01背包)

解题思路&#xff1a; 本题属于01背包问题&#xff0c;使用动态规划 dp[ j ]表示容量为 j 的背包的最大价值 注意&#xff1a; 需要时刻提醒自己dp[ j ]代表的含义&#xff0c;不然容易晕头转向 注意越界问题&#xff0c;且 j 需要倒序遍历 如果正序遍历 dp[1] dp[1 - vo…

java的多态和final关键字

多态&#xff1a; 多态分为对象多态&#xff0c;行为多态 多态的前提&#xff1a; 有继承/实现关系&#xff1b;存在父类引用子类对象&#xff1b;存在方法重写&#xff1b; 注意&#xff1a;多态是对象&#xff0c;行为的多态&#xff0c;java的成员变量不谈多态 这是我写…

将Knife4j所展示请求参数和响应参数转化为TS类型声明

目标&#xff1a;在浏览器控制台输入js代码&#xff0c;将读取页面所展示的请求参数和响应参数&#xff0c;将他们转化为TS的类型声明&#xff0c;并在控制台中输出出来。 将Knife4j所展示请求参数和响应参数转化为TS类型声明 1 找到所需要的元素节点2 转化元素节点3 封装成函…

本地部署的stable diffusion 如何更新controlnet?

stable diffusion 未启动状态 点击“版本管理” 点击“扩展” 找到controlnet&#xff0c;点击右边的“更新”按钮 完成&#xff01;

【软考---系统架构设计师】特殊的操作系统介绍

目录 一、嵌入式系统&#xff08;EOS&#xff09; &#xff08;1&#xff09;嵌入式系统的特点 &#xff08;2&#xff09;硬件抽象层 &#xff08;3&#xff09;嵌入式系统的开发设计 二、实时操作系统&#xff08;RTOS&#xff09; &#xff08;1&#xff09;实时性能…

总结TCP各类知识点

前言 本篇博客博主将详细地介绍TCP有关知识点&#xff0c;坐好板凳发车啦~ 一.TCP特点 1.有连接 TCP传输的过程中类似于打电话的各个过程 2.可靠传输 通过TCP自身的多种机制来保证可靠传输 3.面向字节流 内容是以字节的方式来进行发送与接收 4.缓冲区 TCP有接收缓冲区…

网络安全接入认证-802.1X接入说明

介绍 802.1X是一个网络访问控制协议&#xff0c;它可以通过认证和授权来控制网络访问。它的基本原理是在网络交换机和认证服务器之间建立一个安全的通道&#xff0c;并要求客户端提供身份验证凭据。如果客户端提供的凭据是有效的&#xff0c;交换机将开启端口并允许访问。否则&…

服务器被挖矿了怎么办,实战清退

当我们发现服务器资源大量被占用的时候&#xff0c;疑似中招了怎么办 第一时间重启服务是不行的&#xff0c;这些挖矿木马一定是会伴随着你的重启而自动重启&#xff0c;一定时间内重新霸占你的服务器资源 第一步检查高占用进程 top -c ps -ef 要注意这里%CPU&#xff0c;如果…
最新文章