1

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>
        )
}
T888
  • 69
  • 3
  • 14
  • 1
    Does this answer your question? [Countdown timer in React](https://stackoverflow.com/questions/40885923/countdown-timer-in-react) – Danila Apr 22 '21 at 10:52

2 Answers2

12

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>
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Hey Drew, thanks for the reply, I'm still slow in understanding the logic so I'm going to need some time to read through it. Although, on a side note, I think I understood my mistake, I was looking to both output the date at which the countdown was going to end AND use the numbers from the dates to activate a countdown. – T888 Apr 22 '21 at 09:23
  • Fantastic, this should work great for me, but I do have two questions if you don't mind: - First, why didn;t you use useRef() for the timerId variable, and - what would the math for hours and days look like? – T888 Apr 22 '21 at 10:04
  • 1
    @T888 I didn't use a ref for the timer is because the timer id was completely closed over in the `useEffect` callback function's scope. It doesn't need to be referenced by anything external to the effect callback. For computing days, hours, minutes, and seconds the math is a little different (well, mostly the same, just more of it). Check this [codesandbox](https://codesandbox.io/s/countdown-timer-to-5-minutes-from-current-date-1h4tc?file=/src/App.js). – Drew Reese Apr 22 '21 at 16:09
0

look at

Countdown timer in React

and

https://www.npmjs.com/package/react-countdown-circle-timer

  • 1
    Thanks man, trying to make it work with hooks instead of with packages or constructor classes – T888 Apr 22 '21 at 08:56