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

React.js中useEffect后台不执行,最小化浏览器后秒表暂停如何解决?

问题根因

浏览器为了降低非活跃标签页的性能消耗,会对后台页面的setInterval、setTimeout定时器做节流处理,甚至直接暂停执行。你当前的秒表实现逻辑是依赖每10ms触发一次定时器累加毫秒数,一旦定时器被暂停,计数就会中断,就会出现最小化后计时停止的问题。

另外你当前的代码还有逻辑错误:秒、分的进位判断写在了useEffect的顶层作用域,没有放在定时器回调中,本身的进位逻辑也会出现触发时机不对的问题。

修复方案

放弃基于定时器累加计数的实现,改用时间戳差值计算的方案,只要系统时间正确,不管定时器是否被节流,最终计算出的计时结果都是准确的:

  • 启动时记录当前的起始时间戳,以及暂停时已经累计的计时总时长
  • 每次定时器触发时,用「当前时间戳 - 起始时间戳 + 累计历史时长」得到总计时长,再换算为分、秒、毫秒

修正后完整代码

import { useState, useEffect, useRef } from "react";

export const SW = () => {
  const [totalTime, setTotalTime] = useState(0); // 总计时长,单位毫秒
  const [isOn, setIsOn] = useState(false);
  const startTimeRef = useRef(0); // 存储每次启动的起始时间戳
  const timerRef = useRef(null); // 存储定时器id
  const lastPauseTimeRef = useRef(0); // 存储暂停时累计的总时长

  const start = () => {
    setIsOn(true);
    startTimeRef.current = Date.now();
  };

  const stop = () => {
    setIsOn(false);
    lastPauseTimeRef.current = totalTime;
    clearInterval(timerRef.current);
  };

  const reset = () => {
    setIsOn(false);
    setTotalTime(0);
    lastPauseTimeRef.current = 0;
    clearInterval(timerRef.current);
  };

  useEffect(() => {
    if (isOn) {
      timerRef.current = setInterval(() => {
        const currentOffset = Date.now() - startTimeRef.current;
        setTotalTime(lastPauseTimeRef.current + currentOffset);
      }, 10);
    }
    return () => clearInterval(timerRef.current);
  }, [isOn]);

  // 把总时长转成分、秒、毫秒
  const min = Math.floor(totalTime / 60000);
  const sec = Math.floor((totalTime % 60000) / 1000);
  const mSec = Math.floor((totalTime % 1000) / 10);

  return (
    <div>
      <p>
        {min.toString().padStart(2, "0")}:{sec.toString().padStart(2, "0")}:
        {mSec.toString().padStart(2, "0")}
      </p>
      {!isOn && <button onClick={start}>{!totalTime ? "start" : "resume"}</button>}
      {isOn && <button onClick={stop}>stop</button>}
      <button disabled={!totalTime} onClick={reset}>
        reset
      </button>
    </div>
  );
};

补充说明

  • 用useRef存储定时器、时间戳等值,可以避免频繁触发组件重渲染,也不需要把这些值放到useEffect的依赖项中
  • 只要用户的系统时间没有手动修改,就算浏览器把后台定时器暂停几分钟,重新打开页面后,第一次执行定时器回调计算出的总时长依然是正确的,不会出现计时丢失的问题

内容的提问来源于stack exchange,提问作者Muhammad Rifqi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 08:57:03