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

React定时器启动后,如何实现每次点击减少1秒?

解决定时器点击减秒的问题

你的核心问题是没有维护定时器的截止时间状态,且错误地使用了useMemo定义减秒函数。以下是修复后的完整代码及关键修改说明:

import { useState, useRef, useEffect } from "react";
import Button from "../components/Button";

interface Props {
  setTime: (time: string) => void;
}

function Body({ setTime }: Props) {
  const timerRef = useRef<NodeJS.Timeout | null>(null);
  const [timer, setTimer] = useState("00:00:00");
  // 新增:维护截止时间状态,用于修改和追踪定时器进度
  const [deadline, setDeadline] = useState<Date | null>(null);

  const getTimeRemaining = (targetDate: Date) => {
    const total = targetDate.getTime() - Date.now();
    const seconds = Math.floor((total / 1000) % 60);
    const minutes = Math.floor((total / 1000 / 60) % 60);
    const hours = Math.floor((total / 1000 / 60 / 60) % 24);

    return {
      total,
      hours,
      minutes,
      seconds,
    };
  };

  const updateTimer = () => {
    if (!deadline) return;
    const { total, hours, minutes, seconds } = getTimeRemaining(deadline);

    if (total >= 0) {
      setTimer(
        `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`
      );
    } else {
      // 定时器结束后清空状态
      clearInterval(timerRef.current as NodeJS.Timeout);
      timerRef.current = null;
      setDeadline(null);
    }
  };

  const startTimer = () => {
    // 初始化10秒定时器(对应原代码初始显示的00:00:10)
    const initialDeadline = new Date();
    initialDeadline.setSeconds(initialDeadline.getSeconds() + 10);
    setDeadline(initialDeadline);

    // 清除旧定时器,启动新定时器
    if (timerRef.current) clearInterval(timerRef.current);
    timerRef.current = setInterval(updateTimer, 1000);
    updateTimer(); // 立即更新显示
  };

  const decreaseSecond = () => {
    if (!deadline) return;
    // 创建新日期对象避免直接修改state
    const newDeadline = new Date(deadline);
    newDeadline.setSeconds(newDeadline.getSeconds() - 1);
    setDeadline(newDeadline);
    updateTimer(); // 立即刷新显示
  };

  useEffect(() => {
    setTime(timer);
  }, [timer]);

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

  return (
    <div>
      {timerRef.current ? (
        <Button onClick={decreaseSecond}>Decrease</Button>
      ) : (
        <Button onClick={startTimer}>Start</Button>
      )}
      {/* 可选:显示当前定时器时间 */}
      <div>{timer}</div>
    </div>
  );
}

export default Body;

关键修改说明:

  • 新增deadline状态:将定时器的截止时间存入state,让减秒操作可以直接修改并追踪这个核心变量。
  • 替换useMemo定义减秒函数:decreaseSecond是普通函数,不需要useMemo包裹,这样能保证每次调用都获取最新的deadline状态。
  • 重构定时器逻辑:拆分出updateTimer专门负责计算和更新显示时间,startTimer负责初始化截止时间和启动定时器,逻辑更清晰。
  • 减秒功能实现:创建新的Date对象(避免直接修改state),将秒数减1后更新deadline,并立即调用updateTimer刷新显示,不用等待下一秒定时器触发。
  • 优化内存管理:新增组件卸载时的定时器清理逻辑,避免内存泄漏。
  • 简化时间格式化:用padStart替代冗余的三元判断,代码更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:45:35