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

React中setLap导致秒表启停重置功能故障的排查求助

秒表计次功能故障排查与修复

故障现象

点击计次按钮触发lap_update调用setLap后,stop/reset功能失效,start函数重复创建定时器;未点击计次前功能正常。

原代码片段

const [lap, setLap] = useState([]);

function start(){
       if(!isStart){
            starting = Date.now() - elapsed;
            isStart = true;
            timer = setInterval(update, 1000);  
            console.log("timer is activated");
    }
}

function stop(){
        if(isStart){
            isStart = false;
            clearInterval(timer);
    }
}

function reset(){
        isStart = false;
        elapsed = 0;
        document.getElementById("time").textContent = "00:00:00";
        clearInterval(timer);
        starting = 0;
    }
}

function update(){
        var curr = Date.now();
        elapsed = curr - starting;
        var min = Math.floor(elapsed/(1000*60));
        var sec = Math.floor((elapsed/1000)%60);
        var msec = Math.floor((elapsed%1000)/10);
        var time = [min, sec, msec]
        time = time.map((element) => pad(element));
        var time_str = time[0] + ":" + time[1] + ":" + time[2];
        document.getElementById("time").textContent = time_str;
        console.log(time_str)
    }

const lap_update = (val) => {
        setLap(lap => [...lap, val]);
    }

<div id="lap" onClick={() => lap_update(document.getElementById("time").textContent)}>LAP</div>

故障原因

核心问题是用普通变量管理秒表状态,而非React的状态管理API:

  • React函数组件每次重渲染时,内部的普通变量(isStart、elapsed、starting、timer)会被重新初始化。调用setLap更新状态会触发组件重渲染,这些变量会回到初始值(比如isStart变回false,timer变成undefined)
  • stop/reset函数里的clearInterval拿不到正确的定时器ID,无法停止计时;start函数会误以为计时器未启动,重复创建新定时器,导致重复渲染
  • 直接操作DOM更新时间,会导致状态与UI不同步,进一步加剧问题

修复方案

将所有状态变量替换为React的useState或useRef(根据是否需要触发重渲染选择),移除DOM直接操作:

import { useState, useRef } from 'react';

function Stopwatch() {
  // 用useState管理需要触发UI更新的状态
  const [lap, setLap] = useState([]);
  const [isStart, setIsStart] = useState(false);
  const [elapsed, setElapsed] = useState(0);
  const [displayTime, setDisplayTime] = useState("00:00:00");
  
  // 用useRef存储不需要触发重渲染的变量(保持引用不变)
  const timerRef = useRef(null);
  const startingRef = useRef(0);

  // 补零工具函数
  const pad = (num) => num.toString().padStart(2, '0');

  function start(){
    if(!isStart){
      startingRef.current = Date.now() - elapsed;
      setIsStart(true);
      // 改为10ms间隔,让毫秒位更新更流畅
      timerRef.current = setInterval(update, 10);
      console.log("timer is activated");
    }
  }

  function stop(){
    if(isStart){
      setIsStart(false);
      clearInterval(timerRef.current);
    }
  }

  function reset(){
    setIsStart(false);
    setElapsed(0);
    setDisplayTime("00:00:00");
    setLap([]);
    clearInterval(timerRef.current);
    startingRef.current = 0;
  }

  function update(){
    const curr = Date.now();
    const newElapsed = curr - startingRef.current;
    setElapsed(newElapsed);
    
    const min = Math.floor(newElapsed/(1000*60));
    const sec = Math.floor((newElapsed/1000)%60);
    const msec = Math.floor((newElapsed%1000)/10);
    const timeStr = `${pad(min)}:${pad(sec)}:${pad(msec)}`;
    setDisplayTime(timeStr);
  }

  const lap_update = () => {
    // 直接从状态读取时间,避免DOM操作
    setLap(prevLap => [...prevLap, displayTime]);
  }

  return (
    <div>
      <div id="time">{displayTime}</div>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
      <button onClick={reset}>Reset</button>
      <div id="lap" onClick={lap_update}>LAP</div>
      <div className="lap-list">
        {lap.map((time, index) => (
          <div key={index}>Lap {index+1}: {time}</div>
        ))}
      </div>
    </div>
  );
}

export default Stopwatch;

关键修改点

  1. 状态管理替换:
    • 用useState管理isStart、elapsed、displayTime、lap,确保状态变化能触发正确的UI更新
    • 用useRef存储定时器ID和起始时间,避免组件重渲染时丢失引用
  2. 移除DOM操作:所有UI更新通过状态驱动,不再直接调用document.getElementById
  3. 优化计次逻辑:直接从状态读取当前时间,保证数据一致性
  4. 定时器间隔优化:从1000ms改为10ms,提升毫秒位显示流畅度

内容的提问来源于stack exchange,提问作者Abhrajit Saha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:10:07