简体   繁体   中英

Best way to use setInterval inside React useEffect in countdown timer

newbie to Javascript & React, trying to learn my way through React by building small projects. I'm working on a countdown timer right now, and I'm wondering if there's a better way of handling the pause/stop logic.

Right now, I'm calling setInterval() when my component is no longer in a "Paused" !paused state, and returning a cleanup() function to clear the interval. When I console logged it, it only cleared interval on pause/start.

However, I want to add functionality to automatically stop the timer once it hits zero, which requires adding duration state as a second dependency. When I logged this to console, I realized that my setDuration and clearInterval was now running at every interval, rather than only on the pause/start (it looks like it's calling setInterval() at every "tick" now.

My question is - is this a problem? And is there a better way to do this?

function Timer(props) {
  const [duration, setDuration] = useState(10);
  const [paused, setPaused] = useState(true);

  useEffect(() => {
    let timerId;
    if (!paused) {
      timerId = setInterval(() => {
        setDuration(prev => prev - 1);
      }, 1000);
      // console.log(timerId);
    }

    if (duration === 0) {
      // console.log(`Time's up`);
      clearInterval(timerId);
    }

    return function cleanup() {
      // console.log(`Clearing ${timerId}`);
      clearInterval(timerId);
    };
  }, [paused, duration]);

  const handleClick = (e) => {
    !paused ? setPaused(true) : setPaused(false);
  };

  return (
    <>
      <h3>{duration}</h3>
      <Button paused={paused} onClick={handleClick} />
    </>
  );
}

In your code, duration is changing on every interval and because it is passed as a parameter in dependecy array, it is triggering useEffect on each interval which is causing it to the setDuration and clearInterval to run at each interval.

you can use ternary operator to check if the duration is less than zero then you can automatically set the duration to zero by using setDuration(0)

function Timer(props) {
      const [duration, setDuration] = useState(10);
      const [paused, setPaused] = useState(true);
    
      useEffect(() => {
        let timerId;
        if (!paused) {
          timerId = setInterval(() => {
            setDuration((prev) => prev - 1);
          }, 1000);
          console.log(timerId);
        }
    
        return function cleanup() {
          console.log(`Clearing ${timerId}`);
          clearInterval(timerId);
        };
      }, [paused]);
    
      const handleClick = (e) => {
        !paused ? setPaused(true) : setPaused(false);
      };
    
      return (
        <>
          {duration >= 0 ? <h3>{duration}</h3> : setDuration(0)}
          <button paused={paused} onClick={handleClick} />
        </>
      );
    }

Play around with the code here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM