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
相关推荐
xjt_09014 分钟前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农16 分钟前
Vue 2.3
前端·javascript·vue.js
夜郎king40 分钟前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_2 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞052 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl