使用React useState hook+setInterval实现秒表恢复时额外启动定时器问题
问题原因
该问题是React 18 开发环境严格模式(Strict Mode)的特性导致的:
- React 18 开发环境下,严格模式会主动调用两次状态更新的函数式回调(即你传给
setIntervalID的参数函数),目的是检测更新函数是否为纯函数、是否存在不安全的副作用。 - 你的代码把创建定时器
setInterval这个带副作用的逻辑写在了setIntervalID的回调内部,两次回调执行时拿到的oldId都是更新前的undefined,所以会连续创建两个定时器。 - 最终状态只会保留第二次回调返回的定时器ID(即日志里的12),第一次创建的ID为11的定时器没有被存入状态,后续点击暂停时只会清掉ID为12的定时器,ID为11的定时器就发生了泄露,一直运行导致计数翻倍。
修复方案
核心原则是不要在状态更新的函数式回调中编写带副作用的代码,把创建定时器的逻辑移到回调外部即可,修改后的代码如下:
import React, { useState } from "react"; function StopWatch() { const [count, setCount] = useState(0); const [intervalID, setIntervalID] = useState(undefined); console.log('Rendering, intervalID =', intervalID); return ( <div> <p>Counter: {count}</p> <button onClick={() => { // 不在状态更新回调内创建定时器,先判断再创建 if (intervalID === undefined) { const newId = setInterval(() => { setCount(count => count + 1); }, 1000); console.log('Started Interval with ID =', newId); setIntervalID(newId); } else { console.log('Skipping, IntervalID =', intervalID); } }}> Start </button> <button onClick={() => { if (intervalID !== undefined) { console.log('Stopping IntervalID =', intervalID); clearInterval(intervalID); setIntervalID(undefined); } }}> Pause </button> </div> ); }
内容的提问来源于stack exchange,提问作者utmp
相关产品推荐
相关产品推荐

