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
相关产品推荐
相关产品推荐

