May
May

Reputation: 61

React: Use Interval Not Clearing

I am trying to clear the useInterval function once the joke array has 5 jokes as objects. However, I'm not sure what I'm doing wrong. For full code: https://codesandbox.io/s/asynchronous-test-mp2fq?file=/AutoComplete.js

 const [joke, setJoke] = React.useState([]);

  function useInterval(callback, delay) {
    const savedCallback = useRef();

    let id;

    useEffect(() => {
      savedCallback.current = callback;
      if (joke.length === 5) {
        console.log("5 STORED AND CLEARED INTERVAL");
        return () => clearInterval(id);
      }
    });

    useEffect(() => {
      function tick() {
        savedCallback.current();
      }

      id = setInterval(tick, delay);
      return () => clearInterval(id);
    }, [delay]);
  }

  useInterval(() => {
    // setJoke(joke);
    axios.get("https://api.chucknorris.io/jokes/random").then((res) => {
      setJoke(joke.concat(res.data.value));
      console.log("JOKE: ", joke);
    });

    console.log("Every 5 seconds");
  }, 5000);

enter image description here

Upvotes: 0

Views: 849

Answers (2)

Ori Drori
Ori Drori

Reputation: 191976

Use a ref to store the interval id, to preserve it between re-renders. When the length is 5, call clearInterval instead of returning a clean function that would be called when the component unmounts.

In addition, make the hook agnostic of the actual stop condition by supplying a stop function, and calling it whenever the component re-renders.

function useInterval(callback, delay, stop) {
  const savedCallback = useRef();
  const interval = useRef();

  useEffect(() => {
    savedCallback.current = callback;
    
    if (stop?.()) { // call stop to check if you need to clear the interval
      clearInterval(interval.current); // call clearInterval
    }
  });

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }

    interval.current = setInterval(tick, delay); // set the current interval id to the ref

    return () => clearInterval(interval.current);
  }, [delay]);
}

const Example () => {
  const [joke, setJoke] = React.useState([]);
  
  useInterval(() => {
    // setJoke(joke);
    axios.get("https://api.chucknorris.io/jokes/random").then((res) => {
      setJoke(joke.concat(res.data.value));
      console.log("JOKE: ", joke);
    });

    console.log("Every 5 seconds");
  }, 5000, () => joke.length > 4);
  
  return (
    ...
  );
};

Upvotes: 2

TR3
TR3

Reputation: 387

Try adding in callback to your dependency array

    useEffect(() => {
      savedCallback.current = callback;
      if (joke.length === 5) {
        console.log("5 STORED AND CLEARED INTERVAL");
        return () => clearInterval(id);
      }
    },[callback]);

Upvotes: 0

Related Questions