Search code examples
javascriptreactjsreact-hooksintervals

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


Solution

  • 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 (
        ...
      );
    };