React Timer using seState and Interval not Working

Asked
Active3 hr before
Viewed126 times

8 Answers

timersestateintervalworkingusingreact
90%

Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers , How many iterations to reach the sequence? ,Thanks for contributing an answer to Stack Overflow!, Meta Stack Overflow

Looks like you are mutating state and have an issue with stale enclosure. Use a functional state update to correctly update from the previous state. Move the conditional check on currentTime.seconds into the functional update. You will also want to check and "reset" the seconds to 0 when 59 seconds is reached so that on the next "tick" it is 0 instead of reaching 60.

useEffect(() => {
   let interval;

   if (!finished) {
      interval = setInterval(() => {
         setCurrentTime((currentTime) => {
            if (currentTime.seconds === 59) {
               return {
                  ...currentTime,
                  minutes: currentTime.minutes + 1,
                  seconds: 0
               };
            } else {
               return {
                  ...currentTime,
                  minutes: currentTime.minutes,
                  seconds: currentTime.seconds + 1
               };
            }
         });
      }, 1000);
   } else {
      clearInterval(interval);
   }

   return () => clearInterval(interval);
}, [finished]);
88%

The code above schedules a new interval to run every second inside of the useEffect Hook. This will schedule once the React component mounts for the first time. To properly clear the interval, we return clearInterval from the useEffect Hook, passing in the interval.,For example, the code below schedules a new interval when the React component mounts for the first time. After the React component unmounts the interval is cleared:,Above all, when using setInterval, it is imperative that you clear the scheduled interval once the component unmounts. ,The useEffect function returns the clearInterval method with the scheduled interval passed into it. As a result, the interval is correctly cleared and no longer triggers every second after the component unmounts from the DOM.

useEffect(() => {
   const interval = setInterval(() => {
      console.log('This will run every second!');
   }, 1000);
   return () => clearInterval(interval);
}, []);
load more v
72%

import React, { useState, useEffect } from 'react';

const IntervalExample = () => {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        {seconds} seconds have elapsed since mounting.
      </header>
    </div>
  );
};

export default IntervalExample;


//__________________________________________//
function Counter() {
  let [count, setCount] = useState(0);
  let [delay, setDelay] = useState(1000);

  useInterval(() => {
    // Your custom logic here
    setCount(count + 1);
  }, delay);

  function handleDelayChange(e) {
    setDelay(Number(e.target.value));
  }

  return (
    <>
      <h1>{count}</h1>
      <input value={delay} onChange={handleDelayChange} />
    </>
  );
}
load more v
65%

ITNEXT is a platform for IT developers & software engineers…,What makes intervals a bit tricky in React is that the callback function you pass to them will inevitably close (remember closures?) over the current state of your component. Your interval will see “old” state and not work the way you intended. This is often confusing for new React developers.,Does not follow React design patterns,If multiple parts of your app call setCount , they will each restart the interval.

This code does not work.

export default function App() {    const [count, setCount] = useState(0);    useEffect(() => {        setInterval(() => {        setCount(count + 1);        }, 1000)    }, []);    return (        <div className="App">            <h1>The current count is:</h1>            <h2>{count}</h2>        </div>    );}
75%

If the Clock component is ever removed from the DOM, React calls the componentWillUnmount() lifecycle method so the timer is stopped.,We will tear down the timer in the componentWillUnmount() lifecycle method:,When the Clock output is inserted in the DOM, React calls the componentDidMount() lifecycle method. Inside it, the Clock component asks the browser to set up a timer to call the component’s tick() method once a second.,We also want to clear that timer whenever the DOM produced by the Clock is removed. This is called “unmounting” in React.

load more v
40%

Have a look into this code,,But let us be aware of one important thing here. setInterval method is a closure, so, when setInterval is scheduled it uses the value of the counter at that exact moment in time, which is the initial value of 0. This will make us feel, the state from the useState hook is not getting updated inside the setInterval method.,After this, use the countRef.current value instead of the counter state value inside the function passed to the setInterval method.,Just like the setInterval method, we will use the setTimeout method inside the useEffect hook. We will also clear the timer when the component unmount.

Here we have defined an interval of 1 second(1000 milliseconds) to run a function that prints some logs in the browser console.

const timerId = setInterval(() => {
   console.log('Someone Scheduled me to run every second');
}, 1000);
load more v
22%

Then we may see the value of time isn’t updating every second., 1 Comment on How to Fix a State That is Not Updating When Using React State Hook in setInterval ,Therefore, we can’t just pass in time + 1 to update the value of time .,Now we should see the time display updated every second as we expect.

If we have code like the following:

import { useEffect, useState } from "react";

export default function App() {
  const [time, setTime] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setTime(time + 1);
    }, 1000);
    return () => {
      clearInterval(timer);
    };
  }, []);

  return <div className="App">{time}</div>;
}
load more v
60%

As others have pointed out, the problem is that useState is only called once (as deps = []) to set up the interval:,An alternative to setInterval is to set new interval with setTimeout each time the state is updated:,Actually, this could also be improved so that it doesn't restart the delay when unpaused, but I guess for most uses cases this is good enough.,You can use the alternative form of useState's setter and provide a callback rather than the actual value you want to set (just like with setState):

function Clock() {
  const [time, setTime] = React.useState(0);
  React.useEffect(() => {
    const timer = window.setInterval(() => {
      setTime(prevTime => prevTime + 1); // <-- Change this line!
    }, 1000);
    return () => {
      window.clearInterval(timer);
    };
  }, []);

  return (
    <div>Seconds: {time}</div>
  );
}

ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>
load more v

Other "timer-sestate" queries related to "React Timer using seState and Interval not Working"