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

React实现支持自定义时长、暂停/恢复、重置的倒计时秒表

React倒计时秒表缺陷修复方案

原有代码核心问题

原有实现无法满足需求的根因如下:

  • 未存储定时器实例ID,暂停、重置操作无法清除正在运行的定时器,会出现多定时器叠加、计时速度异常、暂停后仍走时的问题
  • 未单独保存用户初始输入的时间值,重置功能无参考基准
  • 输入值直接绑定展示状态,秒数超过60时不会自动进位换算
  • 状态更新存在闭包陷阱,定时器回调内读取的运行状态不是最新值
  • RESET按钮未绑定任何交互逻辑,无法实现重置效果

修复后完整实现

import { useState, useRef, useEffect } from "react";
import "./styles.css";

export default function App() {
  // 用户输入的原始分、秒值
  const [inputMin, setInputMin] = useState(0);
  const [inputSec, setInputSec] = useState(0);
  // 剩余总秒数(计时核心状态)
  const [remainSeconds, setRemainSeconds] = useState(0);
  // 运行状态标记
  const [isRunning, setIsRunning] = useState(false);
  // 存储初始总秒数,用于重置
  const initialTotalRef = useRef(0);
  // 存储定时器ID,跨渲染周期保存
  const timerRef = useRef(null);

  // 格式化展示分、秒,自动补零
  const displayMin = String(Math.floor(remainSeconds / 60)).padStart(2, '0');
  const displaySec = String(remainSeconds % 60).padStart(2, '0');

  // 组件卸载时清除定时器,避免内存泄漏
  useEffect(() => {
    return () => {
      if (timerRef.current) clearInterval(timerRef.current);
    }
  }, []);

  // 启动倒计时
  const handleStart = () => {
    if (isRunning) return;
    // 计算总秒数,自动处理秒数超60的进位逻辑
    const total = Number(inputMin) * 60 + Number(inputSec);
    if (total <= 0) return;
    
    initialTotalRef.current = total;
    setRemainSeconds(total);
    setIsRunning(true);

    timerRef.current = setInterval(() => {
      setRemainSeconds(prev => {
        if (prev <= 1) {
          clearInterval(timerRef.current);
          setIsRunning(false);
          return 0;
        }
        return prev - 1;
      });
    }, 1000);
  };

  // 暂停/恢复切换
  const handlePauseToggle = () => {
    if (isRunning) {
      // 暂停:清除定时器,更新状态
      clearInterval(timerRef.current);
      setIsRunning(false);
    } else {
      // 恢复:没有剩余时间时不触发
      if (remainSeconds <= 0) return;
      setIsRunning(true);
      timerRef.current = setInterval(() => {
        setRemainSeconds(prev => {
          if (prev <= 1) {
            clearInterval(timerRef.current);
            setIsRunning(false);
            return 0;
          }
          return prev - 1;
        });
      }, 1000);
    }
  };

  // 重置
  const handleReset = () => {
    clearInterval(timerRef.current);
    setIsRunning(false);
    setRemainSeconds(initialTotalRef.current);
  };

  return (
    <div className="App">
      <h1>倒计时秒表</h1>
      <div>
        <label>分钟:</label>
        <input
          type="number"
          min="0"
          value={inputMin}
          onChange={(e) => setInputMin(Number(e.target.value) || 0)}
          disabled={isRunning}
        />
        <label>秒:</label>
        <input
          type="number"
          min="0"
          value={inputSec}
          onChange={(e) => setInputSec(Number(e.target.value) || 0)}
          disabled={isRunning}
        />
      </div>
      <div style={{ margin: '20px 0', fontSize: '32px' }}>
        {displayMin}:{displaySec}
      </div>
      <div>
        <button onClick={handleStart} disabled={isRunning}>START</button>
        <button onClick={handlePauseToggle} disabled={remainSeconds === 0}>
          {isRunning ? 'PAUSE' : 'RESUME'}
        </button>
        <button onClick={handleReset}>RESET</button>
      </div>
    </div>
  );
}

实现说明

  • 秒数超60自动进位逻辑通过总秒数换算实现,例如输入5分90秒时,总秒数为5*60+90=390,自动换算为6分30秒开始倒计时
  • 定时器ID存在useRef中,跨渲染周期不会丢失,可随时清除避免计时错乱
  • 倒计时到0时自动清除定时器、停止运行,不会出现负数时间
  • 运行中禁用输入框,避免计时过程中修改输入值导致逻辑异常
  • 按钮根据当前状态自动禁用无效操作,比如运行中START按钮不可点击,无有效计时时暂停/恢复按钮不可点击

内容的提问来源于stack exchange,提问作者Vivek Joshi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:57:14