Home > Blockchain >  How stop setInterval automatically in React hooks?
How stop setInterval automatically in React hooks?

Time:06-13

I want build a Circular ProgressBar that count at 60 and then automatically stop.

But it can't stop.

I want use React hooks and useEffect

My code here:

https://codesandbox.io/s/nostalgic-khorana-lijdyo?file=/src/App.js:0-686

But the code essence here also:

import React, { useState, useEffect } from "react";
import Circle from "./Circle";
export default function App() {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      if (counter < 60) {
        setCounter((t) => t   1);
        console.log(counter);
      } else {
        console.log(`Why not run?`);
        return () => clearInterval(intervalId);
      }
      return () => clearInterval(intervalId);
    }, 100);
  }, []);

  return (
    <div>
      <div>
        <Circle
          size={250}
          strokeWidth={5}
          percentage={counter}
          color="#229880"
        />
      </div>
    </div>
  );
}

CodePudding user response:

Since you do not include counter in the dependency of useEffect, if (counter < 60) {...} statement will always be true (since the counter is equal to 0 in every re-render in react). The easiest way to get the previous value of the counter would be acquire it in the setCounter function.

useEffect(() => {
  const intervalId = setInterval(() => {
    setCounter((t) => {
      if (t >= 60) clearInterval(intervalId);
      return (t < 60) ? t   1 : t;
    });
  }, 100);
  return () => clearInterval(intervalId);
}, []);

CodePudding user response:

Here is my solution for this

 const [counter, setCounter] = useState(0);
  const counterValid = counter < 60;
  useEffect(() => {
    const intervalId = counterValid && setInterval(() => 
        setCounter((t) => t   1)
      , 100);
      return () => clearInterval(intervalId)
  }, [counterValid]);

  return (
    <div>
      <div>
        <Circle
          size={250}
          strokeWidth={5}
          percentage={counter*(100/60)}
          color="#229880"
        />
      </div>
    </div>

We add counterValid as a dependency to useEffect to re-run the effect whenever the validity of the counter changes.

Also note that your circle expects a 1-100 value for percentage so I multiplied it by 100/60.

CodePudding user response:

Since you are using counter within your hook, you should include counter as a dependency of your useEffect. In addition, you may need to maintain a state for intervalId so you could add another state variable for that.

For example:

  const [counter, setCounter] = useState(0);
  const [intervalId, setIntervalId] = useState(null);

  useEffect(() => {
    // Only call the setInterval if it is not set already.
    if (intervalId) {
      return;
    }
    const newIntervalId = setInterval(() => {
      if (counter < 60) {
        // You may also be incorrectly setting counter, try just incrementing...
        setCounter(counter   1);
        console.log(counter);
      } else {
        console.log(`Why not run?`);
        clearInterval(intervalId);
      }
    }, 100);
    setIntervalId(newIntervalId);

    // You are also returning the callback to clean up the effect in the wrong place.
   // I moved this outside the interval callback for React to have a way to clear the interval during component unmount cycle.
    return () => clearInterval(intervalId);
  }, [ counter, intervalId ]);

EDIT

There is probably another way to achieve what you are doing, what I introduced may be excessive. The issue may actually be your incrementing logic, I would try just updating that first:

// Change this
setCounter((t) => t   1);

// To this
setCounter(counter   1);
  • Related