glownight

返回

一句话结论: useState 更新本身不是异步函数,但它是「批量延迟执行」的,所以代码写起来感觉像异步。


1. 先看最经典的迷惑代码#

const [count, setCount] = useState(0)

function handleClick() {
  console.log(count) // 0
  setCount(count + 1)
  console.log(count) // 还是 0!!!
}
js

明明调用了 setCount,但值立刻拿不到最新的,这就是「看起来异步」。


2. 真正原因:闭包 + 队列机制#

核心 1:useState 是快照式#

  • 每次组件渲染,state 都是一个独立的快照
  • 事件函数里拿到的 count,永远是本次渲染的旧值
  • 调用 setCount 不会修改当前快照,只会触发下一次渲染

核心 2:React 会合并、延迟更新#

React 为了性能,不会一调用 setState 就立刻更新:

  1. 把更新放进队列
  2. 等当前函数执行完
  3. 批量更新 state
  4. 最后重新渲染组件

所以: 在 set 之后立刻打印,拿到的还是「旧快照」。


3. 一句话总结原理#

不是异步,是闭包快照 + 批量延迟更新 → 导致不能同步拿到最新值。

它和真正的异步(Promise/定时器)完全不是一回事


4. 3 种场景帮你彻底理解#

场景 1:同步事件里(看起来异步)#

setCount(1)
console.log(count) // 旧值
js

✅ 原因:更新被延后了

场景 2:set 传函数(立刻用最新值)#

setCount(prev => prev + 1) // 这个能拿到最新值
js

✅ 原因:函数式更新不依赖当前快照

场景 3: useEffect 里(能拿到最新值)#

useEffect(()=>{
  console.log(count) // 最新值
},[count])
js

✅ 原因:渲染完成后才执行


5. 超级好记的口诀#

State 是快照,更新会排队; 当前拿不到,下次渲染见。 想立刻用值,就传函数式。


总结#

useState 更新不是异步,而是:

  1. state 是 immutable 不可变的,每次渲染都是独立快照
  2. setState 只会把更新加入队列,不会立即修改当前值
  3. React 为了性能会批量合并更新,等同步代码执行完才统一更新、重渲染
  4. 所以在同步代码里无法立即获取最新 state,看起来像异步

  • 不是异步,是批量延迟 + 闭包快照
  • 当前渲染的 state 永远不变
  • 想立即用最新值:函数式更新
  • 想监听最新值:useEffect

需要我给你演示如何立刻拿到最新 state的 3 种正确写法吗?

useState 更新为什么看起来有时是异步的?
作者 glownight
发布于 2026年1月24日