如何正确配置React倒计时计时器?解决计时异常与useEffect问题
React倒计时计时器问题修复:停止异常与useEffect优化
问题1:计时器到0后不停止,反向重启
原因分析
- 倒计时结束后,
total值变为负数,计算小时、分钟、秒时出现负数取模,导致显示反向计时数值; - 原判断逻辑依赖
now与timeP的比较,存在时间精度问题,易出现判断失误。
修复方案
- 在
getTimeRemaining函数中,先判断剩余时间是否小于等于0,直接返回全0结果避免负数计算:
const getTimeRemaining = (e) => { var total = (Date.parse(e) - Date.parse(new Date())) / 1000; // 剩余时间<=0时直接返回全0 if (total <= 0) { return { total: 0, days: 0, hours: 0, minutes: 0, seconds: 0, now: Date.parse(new Date()) / 1000, timeP: (Date.parse(createdAt) / 1000) + time }; } var days = Math.floor(total / 86400); total -= days * 86400; var hours = Math.floor(total / 3600) % 24; total -= hours * 3600; var minutes = Math.floor(total / 60) % 60; total -= minutes * 60; var seconds = Math.floor(total % 60); // 取整避免显示小数 var now = Date.parse(new Date()) / 1000 var timeP = (Date.parse(createdAt) / 1000) + time return { total, days, hours, minutes, seconds, now, timeP }; }
- 修改
startTimer函数,剩余时间为0时清除定时器,彻底停止计时:
const startTimer = (e) => { let { total, days, hours, minutes, seconds } = getTimeRemaining(e); if (total > 0) { setTimer( (days < 10 ? '0' + days : days) + ':' + (hours > 9 ? hours : '0' + hours) + ':' + (minutes > 9 ? minutes : '0' + minutes) + ':' + (seconds > 9 ? seconds : '0' + seconds) ) } else { setTimer('00:00:00'); if (Ref.current) { clearInterval(Ref.current); Ref.current = null; } } }
问题2:useEffect无限运行
原因分析
- 原代码未添加
useEffect清理函数,组件卸载或props变化时旧定时器未清除,易重复创建; - 依赖数组为空,未监听
time、createdAt的变化,props更新时无法重新初始化定时器。
修复方案
- 在
useEffect中添加清理函数,组件卸载或重新执行时清除旧定时器; - 补充依赖数组为
[time, createdAt],确保props变化时重新初始化:
useEffect(() => { clearTimer(getDeadTime()); // 清理函数:清除旧定时器 return () => { if (Ref.current) { clearInterval(Ref.current); Ref.current = null; } }; }, [time, createdAt]); // 添加依赖项
完整修复后代码
import React, { useState, useRef, useEffect } from 'react' const Timer = ({time, createdAt}) => { const Ref = useRef(null); const [timer, setTimer] = useState('00:00:00'); const getTimeRemaining = (e) => { var total = (Date.parse(e) - Date.parse(new Date())) / 1000; if (total <= 0) { return { total: 0, days: 0, hours: 0, minutes: 0, seconds: 0, now: Date.parse(new Date()) / 1000, timeP: (Date.parse(createdAt) / 1000) + time }; } var days = Math.floor(total / 86400); total -= days * 86400; var hours = Math.floor(total / 3600) % 24; total -= hours * 3600; var minutes = Math.floor(total / 60) % 60; total -= minutes * 60; var seconds = Math.floor(total % 60); var now = Date.parse(new Date()) / 1000 var timeP = (Date.parse(createdAt) / 1000) + time return { total, days, hours, minutes, seconds, now, timeP }; } const startTimer = (e) => { let { total, days, hours, minutes, seconds } = getTimeRemaining(e); if (total > 0) { setTimer( (days < 10 ? '0' + days : days) + ':' + (hours > 9 ? hours : '0' + hours) + ':' + (minutes > 9 ? minutes : '0' + minutes) + ':' + (seconds > 9 ? seconds : '0' + seconds) ) } else { setTimer('00:00:00'); if (Ref.current) { clearInterval(Ref.current); Ref.current = null; } } } const clearTimer = (e) => { // 先清除旧定时器 if (Ref.current) { clearInterval(Ref.current); Ref.current = null; } // 初始化显示00:00:00 setTimer('00:00:00'); // 启动新定时器 const id = setInterval(() => { startTimer(e); }, 1000) Ref.current = id; } const getDeadTime = () => { let deadline = new Date(createdAt); deadline.setSeconds(deadline.getSeconds() + Math.floor(time)); return deadline; } useEffect(() => { clearTimer(getDeadTime()); return () => { if (Ref.current) { clearInterval(Ref.current); Ref.current = null; } }; }, [time, createdAt]); const onClickReset = () => { clearTimer(getDeadTime()); } return ( <div className="App"> <h2 className='text-3xl lg:text-4xl text-themeYellow bg-clip-text text-center bg-black'>{timer}</h2> </div> ) } export default Timer;
内容的提问来源于stack exchange,提问作者ethereumboy
相关产品推荐
相关产品推荐

