Search code examples
javascriptreactjsreact-hooksnext.jstimer

How to correctly configure this React timer?


I've been searching for an answer to this question for long, however no one here has been able to give me a clear answer yet.

I've got a timer (shown below), however, instead of stopping at 0, the timer instead starts counting down from 24 hours.

Does anyone have any idea how I can stop this timer and zero? Also, the useEffect keeps running like crazy and I'd really appreciate it if someone can help me out with that as well.

Thanks!

CODE:

import { create } from 'domain';
import React, { useState, useRef, useEffect } from 'react'
import { useMoralisQuery } from "react-moralis";

//stop useEffect from running infinitely

const Timer = ({time, createdAt}) => {

    const Ref = useRef(null);

    const [timer, setTimer] = useState('00:00:00');
    const [timerOn, setTimerOn] = useState(false)
    const number = 1000


    const getTimeRemaining = (e) => {
        var total = (Date.parse(e) - Date.parse(new Date())) / 1000; 

        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 = total % 60;

        var now = Date.parse(new Date()) / 1000

        var timeP = (Date.parse(createdAt) / 1000) + time

        // console.log(timeP)
        // console.log(now)

        return {
            total, days, hours, minutes, seconds, now, timeP
        };
    }


    const startTimer = (e) => {
        let { total, days, hours, minutes, seconds, now, timeP }
                    = getTimeRemaining(e);
        if (now < timeP) { //try total > now 

            setTimer(
                (days < 1 ? '00' : days)  + ':' +
                (hours > 9 ? hours : '0' + hours) + ':' +
                (minutes > 9 ? minutes : '0' + minutes) + ':'
                + (seconds > 9 ? seconds : '0' + seconds)
            )
        }

        if (now > timeP) {
            setTimer('00:00:00') 
        }
    }

    const clearTimer = (e) => {

        setTimer('00:00:00');

        if (Ref.current) clearInterval(Ref.current);
        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()); 
    }, []);

    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;

PS: I got the code from here: https://www.geeksforgeeks.org/how-to-create-a-countdown-timer-using-reactjs/


Solution

  • Hmmm...can't seem to pinpoint the exact cause of your bug. However if your question is how to build a countdown timer, this is how I'd approach it.

    1. Create 4 different pieces of state to store the different segments of the countdown i.e. (days, hours, minutes, seconds).
        const [days, setDays] = React.useState('00');
        const [hours, setHours] = React.useState('00');
        const [minutes, setMinutes] = React.useState('00');
        const [seconds, setSeconds] = React.useState('00');
    
    1. Create a useEffect to handle the logic behind the countdown timer.
        React.useEffect(() => {
            //@: get the time in milliseconds towards the due date
            const dueDate = new Date("Jan 15, 2023 00:00:00").getTime();
    
            //@: create an interval to countdown every 1 sec
            const timerInterval = setInterval(() => {
                //@: get the current date in milliseconds
                const now = new Date().getTime();
                //@: calculate the time elapsed by subtracting the due date by the current date
                const timeElapsed = dueDate - now;
    
                //@: get the remaining days, hours, minutes and secs from the time elapsed calculated above
                const dd = Math.floor(timeElapsed / (1000 * 60 * 60 * 24));
                const hh = Math.floor((timeElapsed % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
                const mm = Math.floor((timeElapsed % (1000 * 60 * 60)) / (1000 * 60));
                const ss = Math.floor((timeElapsed % (1000 * 60)) / 1000);
    
                //@: if the time elapsed is less than 0, it means you've passed the due date so clear the interval and stop the timer.
                //@: else set the timers accordingly
                if(timeElapsed < 0){
                    clearInterval(timerInterval);
                }
                else{
                    setDays(dd);
                    setHours(hh);
                    setMinutes(mm);
                    setSeconds(ss);
                }
            }, 1000);
    
            //@: cleanup function to clear any intervals once this component unmounts
            return () => {
                clearInterval(timerInterval);
            }
        }, []);
    
    1. Render out the different timers in your JSX.
    <div>{days} days : {hours} hours : {minutes} mins : {seconds} secs</div>
    

    You could take this a step further by encapsulating this whole logic within a component called Countdown which takes in a date string prop. So it would look like this.

    const Countdown = ({releaseDate}) => {
        const [days, setDays] = React.useState<string | number>('00');
        const [hours, setHours] = React.useState<string | number>('00');
        const [minutes, setMinutes] = React.useState<string | number>('00');
        const [seconds, setSeconds] = React.useState<string | number>('00');
    
        React.useEffect(() => {
            const dueDate = new Date(releaseDate).getTime();
    
            const timerInterval = setInterval(() => {
                const now = new Date().getTime();
                const timeElapsed = dueDate - now;
    
                const dd = Math.floor(timeElapsed / (1000 * 60 * 60 * 24));
                const hh = Math.floor((timeElapsed % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
                const mm = Math.floor((timeElapsed % (1000 * 60 * 60)) / (1000 * 60));
                const ss = Math.floor((timeElapsed % (1000 * 60)) / 1000);
    
                if(timeElapsed < 0){
                    clearInterval(timerInterval);
                }
                else{
                    setDays(dd);
                    setHours(hh);
                    setMinutes(mm);
                    setSeconds(ss);
                }
            }, 1000);
    
            return () => {
                clearInterval(timerInterval);
            }
        }, [releaseDate]);
    
        return (
         <div>{days} days : {hours} hours : {minutes} mins : {seconds} secs</div>
        )
    }
    

    Then call the component in whatever page you need it like so;

       <Countdown releaseDate="Jan 15, 2023 00:00:00" />
    

    Hopefully that helps :)