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

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

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

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

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

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

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

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

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

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

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

function Son(props){
  return(
    
{props.name}
) }

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

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

破案:

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

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

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

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

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

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

最后摘录一段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 在适当的时机才进行批量更新,而不是立即执行更新。它会等待当前渲染过程结束,然后在下一次渲染前处理队列中的更新请求。

你可能感兴趣的:(前端,bug,前端,javascript,reactjs)