React中useState更新后无法立即获取新值的原因咨询
嗨,这事儿我太懂了!你遇到的问题本质是React的状态更新是异步批量执行的,不是调用setter之后就立马同步更新状态的。给你掰扯清楚背后的逻辑:
问题到底出在哪?
当你在TimeChange里调用setTime(t)的时候,React并不会立刻把time改成新值,而是把这次更新丢进一个“更新队列”里攒着。等当前的事件处理函数(也就是你这个TimeChange)跑完之后,React才会一次性处理队列里的所有更新,这时候才会真的更新time的状态,然后触发组件重新渲染。
所以你调用setTime(t)之后马上console.log(time),这时候time还是更新前的旧值(也就是你一开始设的11:15)。等你第二次输入12:30的时候,第一次的状态更新已经完成了,这时候打印的就是第一次更新后的12:00——这就解释了你看到的控制台输出顺序为啥和预期不一样。
怎么才能拿到更新后的状态?
如果你要在状态更新后立刻用新值做事儿,有两个常用的办法:
1. 用useEffect监听状态变化
useEffect会在状态更新、组件重新渲染之后执行,所以把需要用新值的逻辑放在这里就对了:
import { useState, useEffect } from 'react'; // 你的其他代码不变 // 监听time的变化,每次更新后都会触发这里的逻辑 useEffect(() => { console.log(time, ': is set'); }, [time]); // 只有time变了才会跑这个回调 let TimeChange = (t) =>{ console.log('recieved time is:', t); setTime(t); // 这里不用再直接打印time啦,交给useEffect处理 }
改完之后,控制台输出就会和你预期的一样:
recieved time is: 12:00 12:00 : is set recieved time is: 12:30 12:30 : is set
2. 函数式更新(适合依赖旧状态的场景)
如果你的场景是需要基于当前的旧状态来算新状态(比如计数器加1这种),可以用函数式更新的方式,这样能确保拿到最新的旧值:
let TimeChange = (t) =>{ console.log('recieved time is:', t); // 用函数的方式更新,prevTime就是当前的旧状态值 setTime(prevTime => { console.log('旧时间:', prevTime, ',新时间:', t); return t; }); }
不过这个方法更适合需要依赖旧状态的场景,你当前的需求用第一种useEffect的方式就足够啦。
最后总结一下
React搞异步批量更新是为了优化性能,避免组件频繁渲染。记住:调用状态更新器之后,别指望立刻拿到新状态,要么用useEffect监听变化,要么用函数式更新处理依赖旧状态的逻辑。
内容的提问来源于stack exchange,提问作者Hariwarshan

