React 的闭包陷阱 + 状态异步更新机制

🔹1. 为什么 useState 更新了,但打印是旧值?

scss 复制代码
const [count, setCount] = useState(0);

const handleClick = () => {
  setCount(count + 1);
  console.log("count:", count); // ❌ 打印的还是旧值
};

原因:

  • setCount 并不会立刻修改 count,而是 调度一次重新渲染
  • 在当前函数调用上下文里,count 还是旧的。
  • 下一次渲染函数执行时,count 才会变成新值。

👉 React 的 state 更新是异步的、批量的


🔹2. 为什么 useEffect 里总是打印上一次的值?

scss 复制代码
useEffect(() => {
  console.log("count in effect:", count);
}, [count]);

这个逻辑其实是正确的

  • useEffect 执行时机是 DOM 更新后
  • 打印的值就是本次 render 的 count,但因为 console.log 常和点击事件混用,很多人会以为"怎么总是落后一步"。

示例:

rust 复制代码
// 初始 count=0
点击按钮 -> setCount(1)
-> render(count=1)
-> DOM 更新
-> useEffect 执行,打印 1 ✅

如果你在 点击回调里打印effect 里打印对比,就会发现一个"延迟一拍"的错觉。


🔹3. 怎么解决/避免?

✅ 方法1:用 函数式更新 拿到最新值

ini 复制代码
setCount(prev => {
  console.log("最新 count:", prev + 1);
  return prev + 1;
});

这样不会受闭包影响,永远基于最新状态更新。


✅ 方法2:在 useEffect 里观察变化

scss 复制代码
useEffect(() => {
  console.log("count 更新:", count);
}, [count]);

更符合 React 思路:状态变化 → 副作用


✅ 方法3:如果确实要获取"最新的值",用 useRef

ini 复制代码
const countRef = useRef(count);

useEffect(() => {
  countRef.current = count; // 同步最新值
}, [count]);

// 在任何地方访问 countRef.current 都是最新的

🔹4. 总结口诀

  1. setState 不会立即更新,打印的还是旧值
  2. useEffect 打印的其实是对的值,只是它本来就滞后执行
  3. 想要拿到最新值 → 用函数式更新或 useEffect/useRef
相关推荐
程序员黑豆5 分钟前
鸿蒙应用开发教程:以红绿灯切换为例,掌握条件渲染的核心用法
前端·harmonyos
猫32813 分钟前
echarts 日常问题解决
前端·javascript·echarts
新中地GIS开发老师16 分钟前
地信职业百科④:GIS开发工程师
前端·数据库·gis·webgis·三维gis开发
我是大卫25 分钟前
【图】React源码解析-从数据结构、调度器、源码设计模式到状态计算引擎,深挖useReducer原理
前端·react.js·源码
willes32 分钟前
列表中的倒计时组件
前端
LVZ33 分钟前
用了半年 AI 写代码,最烦的不是代码,是环境
前端·后端·开源
polaris_tl34 分钟前
一行 `compress: false`,为什么让 SSE 首包恢复实时返回
前端
海边的云36 分钟前
别再让 AI 瞎改代码:一套让大模型「收敛」的前端专家 Skill》
前端
kisshyshy36 分钟前
给端侧大模型装上“发动机”:React 合成事件 + 进度条组件全解
前端·react.js·node.js
hunterandroid1 小时前
DataStore 工程化实践:迁移、并发更新与异常恢复
android·前端