Search code examples
reactjstimersetintervalclearinterval

How to start the timer from 0 on page refresh in react app


I've a setInterval timer in useEffect and I want the timer to start from zero on page refresh. But it's not.

const [timer, setTimer] = useState(0)

useEffect(() => {
    const timer = setInterval(() => {
      const d = new Date(),
        seconds = d.getMinutes() * 60 + d.getSeconds(),
        totalTime = 60 * 6,
        tL = totalTime - (seconds % totalTime),
        r = `${parseInt(tL / 60, 10)}:${tL % 60}`;

     setTimer(r);
}, 1000);
return () => clearInterval(timer);
}, []);

Solution

  • If you want a simple countdown, you can do something like this (i.e. derive your state from the number of seconds passed):

    function App() {
      const [secondsPassed, setSecondsPassed] = React.useState(0);
    
      const startTime = 60 * 6;
      const timeLeft = startTime - (secondsPassed % startTime);
    
      const minuteCount = ~~(timeLeft / 60);
      const secondsCount = timeLeft % 60;
      const countdown = `${minuteCount}:${secondsCount.toLocaleString("en-US", {
        minimumIntegerDigits: 2
      })}`;
    
      React.useEffect(() => {
        const timer = setInterval(() => {
          setSecondsPassed(sp => sp + 1);
        }, 1000);
        return () => clearInterval(timer);
      }, []);
    
      return <div > {
        countdown
      } < /div>;
    }
    
    ReactDOM.createRoot(document.getElementById("root")).render( < App / > );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
    
    <div id="root"></div>

    However, using setInterval() to increment your timer is actually somewhat of a naive approach because it's not guaranteed to execute immediately after the specified time has passed (if the main thread is busy). The more robust approach is to derive the seconds passed from some start time value like so:

    function App() {
      const startNow = React.useMemo(() => Date.now(), []);
      const [now, setNow] = React.useState(startNow);
    
      const secondsPassed = ~~((now - startNow) / 1000);
    
      const startTime = 60 * 6;
      const timeLeft = startTime - (secondsPassed % startTime);
    
      const minuteCount = ~~(timeLeft / 60);
      const secondsCount = timeLeft % 60;
      const countdown = `${minuteCount}:${secondsCount.toLocaleString("en-US", {
        minimumIntegerDigits: 2
      })}`;
    
      React.useEffect(() => {
        const timer = setInterval(() => setNow(Date.now()), 1000);
        return () => clearInterval(timer);
      }, []);
    
      return <div > {
        countdown
      } < /div>;
    }
    
    
    ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
    
    <div id="root"></div>