You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 06:57:00