一、引子
先来看一段代码,你能说出这段代码的问题在哪吗?
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount(count + 1);
}, 1000);
return () => clearTimeout(timer);
}, []);
正确答案:
这段代码存在闭包陷阱,会导致 setTimeout 中的回调函数捕获到的 count 值始终是初始值 0,最终定时器只会执行一次,count 永远停留在 1。
问题分析
- 依赖数组为空:
useEffect 的依赖数组为 [],意味着它只在组件挂载时执行一次。此时 count 的初始值为 0,被闭包捕获到 setTimeout 的回调函数中。 - 闭包导致的 stale 值:
当定时器触发时(1 秒后),回调函数使用的是捕获时的 count 值(即 0),而非最新值。因此每次执行 setCount(count + 1) 实际都是 setCount(0 + 1),导致 count 永远为 1。 - 清除定时器的副作用:
虽然每次都会清除前一个定时器,但由于依赖数组为空,useEffect 不会重新执行,因此新的定时器不会被创建,后续更新被中断。
解决方案
- 使用函