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

如何正确配置React倒计时计时器?解决计时异常与useEffect问题

React倒计时计时器问题修复:停止异常与useEffect优化

问题1:计时器到0后不停止,反向重启

原因分析

  • 倒计时结束后,total值变为负数,计算小时、分钟、秒时出现负数取模,导致显示反向计时数值;
  • 原判断逻辑依赖now与timeP的比较,存在时间精度问题,易出现判断失误。

修复方案

  1. 在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
    };
}
  1. 修改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更新时无法重新初始化定时器。

修复方案

  1. 在useEffect中添加清理函数,组件卸载或重新执行时清除旧定时器;
  2. 补充依赖数组为[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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:01:17