React.js使用useEffect Hook更新自增计数器卡顿问题
问题根因
你的代码有4个核心bug,直接导致定时器堆积、逻辑失效,出现卡顿、数值跳变:
- 定时器只建不清,越积越多:
useEffect依赖是counter,每次counter变化都会重新执行effect,每次执行都会新建一个2秒触发的定时器,但你从来没有保存旧定时器的ID做清理,运行几秒后就会有几十个甚至上百个定时器同时跑,同时修改counter值,自然会跳变、卡顿。 - 判断条件语法写错:你写的
counter => 1200是定义箭头函数,根本不是大于等于判断(正确的大于等于是>=)。JS里函数本身转布尔值永远为真,这个if的前半段判断完全失效。 - 清除定时器的写法完全无效:
clearInterval必须传入对应setInterval返回的定时器ID才能清掉目标定时器,你代码里clearInterval(setInterval(handleCounter, 2000))的逻辑是:先新建一个定时器,立刻把这个新定时器的ID传进去清掉,之前创建的所有旧定时器根本没被处理,会一直在后台运行。 - 状态更新踩了闭包陷阱:
handleCounter里直接写setCounter(counter + 1),定时器触发时拿到的是创建那个定时器时刻闭包捕获的旧counter值,多个定时器同时触发时会反复基于旧值计算,出现数值跳变、增长顺序混乱的问题。
基础修复代码
const [counter, setCounter] = useState(1200) useEffect(() => { // 数值到上限直接退出,不创建定时器 if (counter >= 1364) return // 保存当前定时器ID,用于后续清理 const timerId = setInterval(() => { // 用函数式更新拿最新值,避免闭包旧值问题 setCounter(prev => prev + 1) }, 2000); // effect重跑/组件卸载时,清理当前定时器,避免堆积 return () => clearInterval(timerId) }, [counter])
更优写法(无需重复重建定时器)
上面的写法每次counter变化都会重建定时器,其实可以把依赖去掉,把判断逻辑放到状态更新里,性能更好:
const [counter, setCounter] = useState(1200) useEffect(() => { const timerId = setInterval(() => { setCounter(prev => { // 数值到上限时清除定时器,不再更新 if (prev >= 1364) { clearInterval(timerId) return prev } return prev + 1 }) }, 2000); // 组件卸载时清理定时器 return () => clearInterval(timerId) }, [])
内容的提问来源于stack exchange,提问作者Khubaib Shoukat
相关产品推荐
相关产品推荐

