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

[bug修复]状态数据在useEffect初始化时更新无效

(bug修复类型的博客还是用汉语写捏)

前两天在做一个管理页面前端的时候,出现了这样的问题

function Son(props){const [a,seta]=useState(0)useEffect(()=>{seta(props.name)},[])return(<div>{a}</div>)
}

这是当时情况的一个简单复现,我在父组件中向子组件传递数据,并且想把这个数据保存在状态里面

理想很丰满,现实很骨干。在子组件的位置,返回结果仍然是0,也就是我们一开始是用useState的初始状态。就好像useEffect内部的set函数没有执行一样。

一开始的想法是useEffect本身是异步方法,因为这个钩子函数更多需要承担一些dom操作,接口获取,发送网络请求等等异步操作。所以这个东西会不会是因为异步,而产生影响。

但是这个想法有两个错误点,首先state状态类似vue中的data是响应样式的,就算是异步操作也是会执行并且发生响应样式的修改,最多等几ns的问题(当然这个想法也是有问题的,后面会说)

其次,就算我们换成了同步的useLayoutEffect函数,也是一个结果

第二个想法是,useEffect函数的执行顺序,但是这个想法和第一个想法的结果大同小异

 但是如果我不保存到状态内,而是直接使用传进来的数据,就瞬间变成了正常的响应状态!

function Son(props){return(<div>{props.name}</div>)
}

所以可以确定,问题出在state的状态没有及时更新!

但是问题不是出在hook函数本身,而是我们在内部调用的set函数。

破案:

 众所周知,useState返回的结果是一个数值,和一个异步回调函数。

如果是在js中,回调函数是在一个叫做事件队列的结构中实现的,当call stack中的上下文执行结束以后,会按照某个顺序执行存在队列中的回调函数

但是在react中,作出了一点小小的改变,回调函数其实并不是按照上下文结束以后,自然执行的。而是需要等待某个时机,这个时机其实就是重新渲染。

因此,我们像一开始那样写代码的时候,就会出现这种情况。

破解方法其实很简单,我们只要在useEffect函数的监听对列中,加入传进来的数据,这样子就可以触发重新渲染这个条件,保持了组建传递的数据可变性质

function Son(props){const [a,seta]=useState(0)useEffect(()=>{seta(props.name)},[props.name])return(<div>{a}</div>)
}

最后摘录一段gpt的回复

React 在适当的时机进行批量更新,而不是像普通 JavaScript 一样遵循自然执行的时间队列。

React 使用一种称为 "调度" 的机制来管理状态更新和组件的重新渲染。当你调用 `setState`(或 `useState` 的更新函数)时,React 会将更新请求添加到调度队列中。然后,在适当的时机,React 会处理队列中的更新请求,并进行批量更新。

React 通常会在以下情况下触发批量更新:

1. 在 React 事件处理函数中:当你在事件处理函数中调用 `setState`,React 会将更新请求添加到队列中,并在事件处理函数执行完成后进行批量更新。

2. 在生命周期方法和钩子函数中:在组件的生命周期方法(如 `componentDidMount`、`componentDidUpdate` 等)和函数组件的钩子函数(如 `useEffect`、`useLayoutEffect` 等)中调用 `setState`,React 也会将更新请求添加到队列中,并在适当的时机进行批量更新。

3. 在异步操作中:如果在异步操作的回调函数中调用 `setState`,React 会将更新请求添加到队列中,并在异步操作完成后进行批量更新。

在这些情况下,React 会等待当前的 JavaScript 执行栈为空(即当前任务结束)以及浏览器的绘制(渲染)过程即将开始时,才会执行批量更新。这样可以最大程度地优化性能,避免不必要的重复渲染。

所以,你观察到的现象是因为 React 在适当的时机才进行批量更新,而不是立即执行更新。它会等待当前渲染过程结束,然后在下一次渲染前处理队列中的更新请求。

http://www.lryc.cn/news/120393.html

相关文章:

  • 使用 API Gateway Integrator 在 Quarkus 中实施适用于 AWS Lambda 的 OpenAPI
  • 【JVM】JVM中的分代回收
  • C# Linq源码分析之Take方法
  • 从后往前读取列表的方法
  • 数据库--数据类型
  • 小型双轮差速底盘机器人实现红外跟随功能
  • TCP协议网络编程 回显服务器,客户端实现
  • 3.4 Spring MVC注解
  • OpenCV实例(八)车牌字符识别技术(三)汉字识别
  • 运维监控学习笔记2
  • 【深度学习】遗传算法[选择、交叉、变异、初始化种群、迭代优化、几何规划排序选择、线性交叉、非均匀变异]
  • 【小吉带你学Git】讲解GitHub操作,码云操作,GitLab操作
  • nginx基础
  • 【Windows API】获取卷标、卷名
  • 通过MATLAB自动产生Hamming编译码的verilog实现,包含testbench
  • swager web服务无法显示问题
  • 代码随想录训练营day18 二叉树
  • 图像的平移变换之c++实现(qt + 不调包)
  • 云原生K8S------Yaml文件详解
  • 测试开发环境安装
  • 微信小程序如何引入Iconfont
  • php使用get和post传递数据出现414 Request-URI Too Large的解决方案
  • 复现大华智慧园区综合管理平台SQL注入漏洞
  • 【uniapp】uniapp设置安全区域:
  • Grafana技术文档--基本安装-docker安装并挂载数据卷-《十分钟搭建》-附带监控服务器
  • 24大连交通大学软件工程813题库
  • 数据治理-组织变革
  • html的语义化
  • 8/12 题解
  • 九耶丨阁瑞钛伦特-产品经理面试题