🔹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. 总结口诀
setState
不会立即更新,打印的还是旧值。useEffect
打印的其实是对的值,只是它本来就滞后执行。- 想要拿到最新值 → 用函数式更新或
useEffect
/useRef
。