ReactJS中状态时变问题:进度值无法正常递增的排查与解决
React状态更新异常:progressValue始终显示0的问题分析
我希望在React中每500毫秒将状态值progressValue从0递增到100,为此编写了如下代码:
const [progressValue, setProgressValue] = React.useState<number>(0) const tick = () => { console.log("progressValue: " + progressValue) if (progressValue >= 100) { setProgressValue(100) } else { const newValue = progressValue + 0.1 setProgressValue(newValue) setTimeout(tick, 500) } } const onPlayClicked = () => { setProgressValue(0) tick() }
但控制台日志始终输出:
progressValue: 0 progressValue: 0 progressValue: 0 ...
问题原因
这是因为闭包捕获了过期的状态值。当调用onPlayClicked时,tick函数被创建,此时它捕获的progressValue是初始值0。之后每次通过setTimeout调用tick时,这个函数依然引用的是最初捕获的那个progressValue,永远为0。所以每次都会执行newValue = 0 + 0.1,并再次设置定时器,导致日志一直输出0。
解决方法(用户最终实现)
最终通过以下代码解决了该问题:
const [progressValue, setProgressValue] = React.useState<number>(0) React.useEffect(() => { const interval = setInterval(() => { if (progressValue < 100) { setProgressValue((prevVal) => prevVal + 1); } }, 500); return () => clearInterval(interval); }, [progressValue]);
解决思路解析
- 使用
setInterval替代嵌套的setTimeout,避免重复创建闭包捕获旧状态 - 调用
setProgressValue时采用函数式更新:(prevVal) => prevVal + 1,这种方式能确保每次获取到的是最新的状态值,不受闭包影响 - 通过
useEffect监听progressValue的变化,状态更新时自动销毁旧定时器并创建新定时器(注:如果仅使用函数式更新,也可以将依赖数组设为空,只在组件挂载时创建一次定时器,进一步优化性能)
内容的提问来源于stack exchange,提问作者Benvorth
相关产品推荐
相关产品推荐

