Search code examples
javascriptreactjsreact-hookscountdowncountdowntimer

Countdown timer to 5 minutes from current date


new to react here. I'm trying to output a countdown timer to a date that is set to 5 minutes from now. But all I was able to get was either the countdown rubber banding, or static on 5 minutes or 0. Currently, it is static on 00:00:00:00. Thanks

EDIT: I'm trying to make it work with hooks instead of through a constructor class.

import AButton from './aButton'; import { useState, useRef, useEffect} from 'react';

export default function Countdown() {
        
        const [addTime, setAddtime] = useState();
        const [countdown, setCountdown] = useState((new Date()).toLocaleTimeString());

        const [timerDays, setDays] = useState('00');
        const [timerHours, setHours] = useState('00');
        const [timerMinutes, setMinutes] = useState('00');
        const [timerSeconds, setSeconds] = useState('00');

        let interval = useRef();

        const oldDateObject = new Date();
        const sourceDate = oldDateObject.getTime(); // date - 300000
        const diff = 5;
        //this gives use the unix timestamp of the future date which is the current date + 5 minutes
        const futureDate = new Date(oldDateObject.getTime() + diff*60000); //300000
        

        const convertToDate = (date, hours, minutes, seconds) => {
                
        
        date = new Date(sourceDate * 1000);
        // Hours part from the timestamp
        hours = date.getHours();
        // Minutes part from the timestamp
        minutes = ("0" + date.getMinutes()).substr(-2);
        // Seconds part from the timestamp
        seconds = ("0" + date.getSeconds()).substr(-2);

        return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
        }

        const startTimer = () => {
                const timeUpdate = new Date().getTime(convertToDate);
        interval = setInterval(() => {  
        const now = new Date().getTime();
        const timeDifference = timeUpdate - now;
        
        //this converts the unix code of the future date to each clock element every second
        const days = Math.floor(timeDifference / (24 * 60 * 60 * 1000));
        const hours = Math.floor(timeDifference % (24 * 60 * 60 * 1000) / (60 * 60 * 1000));;
        const minutes = Math.floor(timeDifference % (60 * 60 * 1000) / (60 * 1000));;
        const seconds = Math.floor(timeDifference % (60 * 1000) / 1000);;
        

        if (timeDifference < 0) {
                clearInterval(interval.current);
        } else {
                setDays(days);
                setHours(hours);
                setMinutes(minutes);
                setSeconds(seconds);
        }

        }, 1000);
};

useEffect(() => {
        startTimer();
        return () => { clearInterval(interval.current); 
        };
});
        
        return (
        <div>
        <h1>{countdown === null ? <span style={{padding: '50px'}}></span> : countdown}</h1>
        <button onClick={ () => setCountdown(countdown !== null ? null : ( new Date()).toLocaleTimeString())}>{countdown === null ? 'Show' : 'Hide'}</button>
        <div>----------------</div>
        <h1>{timerDays} : {timerHours} : {timerMinutes} : {timerSeconds}</h1>
        <button>Activate countdown</button>
        </div>
        )
}

Solution

  • I think you are making it way more complicated than it needs to be.

    Store a state of a 5 minute countdown (in seconds) and a second piece of state to start/run the timer.

    When the timer is started seed the countdown state with 5 minutes (it will expire from the current point in time).

    Compute the derived and formatted time from the single countdown state.

    function App() {
      const [countDown, setCountDown] = React.useState(0);
      const [runTimer, setRunTimer] = React.useState(false);
    
      React.useEffect(() => {
        let timerId;
    
        if (runTimer) {
          setCountDown(60 * 5);
          timerId = setInterval(() => {
            setCountDown((countDown) => countDown - 1);
          }, 1000);
        } else {
          clearInterval(timerId);
        }
    
        return () => clearInterval(timerId);
      }, [runTimer]);
    
      React.useEffect(() => {
        if (countDown < 0 && runTimer) {
          console.log("expired");
          setRunTimer(false);
          setCountDown(0);
        }
      }, [countDown, runTimer]);
    
      const togglerTimer = () => setRunTimer((t) => !t);
    
      const seconds = String(countDown % 60).padStart(2, 0);
      const minutes = String(Math.floor(countDown / 60)).padStart(2, 0);
    
      return (
        <div className="App">
          <div>
            Time: {minutes}:{seconds}
          </div>
    
          <button type="button" onClick={togglerTimer}>
            {runTimer ? "Stop" : "Start"}
          </button>
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(
        <App />,
      rootElement
    );
    .App {
      font-family: sans-serif;
      text-align: center;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <div id="root"></div>