Home > Software design >  react setInterval does not end
react setInterval does not end

Time:12-01

After I set the setIsRunning(false) in the else statement, it is supposed to go out console.log('final call'). but it keeps calling console.log('running'). anyone know what I am doing wrong here and how this can be fixed

  const [val, setval] = useState(0)
  const [isRunning, setIsRunning] = useState(true)

  useEffect(() => {
    let intervalId
    if (isRunning) {
      intervalId = setInterval(() => {
        if (val <= 100) {
          setval((t) => t   5)
        } else {
          console.log('call')
          setIsRunning(false)
        }
        console.log('running') // This is being called continuously
      }, 50)
    } else {
      console.log('final call')
    }
    return () => clearInterval(intervalId)
  }, [isRunning])

Once the val reaches 100 I want the setTimeout to stop, but at the same time i dont want to use val in my dependency. I want the interval to be cleared so that it does not run anymore

CodePudding user response:

Try my code

  const [val, setval] = useState(0);
  const [isRunning, setIsRunning] = useState(true);

  useEffect(() => {
    let intervalId;
    if (isRunning) {
      intervalId = setInterval(() => {
        setval((t) => {
          if (t <= 100) {
            return (t  = 5);
          } else {
            setIsRunning(false);
            clearInterval(intervalId);
            return t;
          }
        });
        console.log('running'); // This is being called continuously
      }, 50);
    } else {
      console.log('final call');
    }
    return () => clearInterval(intervalId);
  }, [isRunning]);

CodePudding user response:

You have 2 problems in your code.

  1. Don't pass state as the dependency of the useEffect.

  2. Your if condition must be in the setInterval not out of it.

And as a suggestion, use Ref instead of State.

So this would work as expected:

const valRef = useRef(0)
const isRunningRef = useRef(true);

useEffect(() => {
  let intervalId;
  intervalId = setInterval(() => {
    if (isRunningRef.current) {
      if (valRef.current <= 100) {
        valRef.current  = 5;
      } else {
        console.log('call');
        isRunningRef.current = false;
      }
      console.log('running');
    } else {
      console.log('final call');
      clearInterval(intervalId);
    }
  }, 50)
  return () => clearInterval(intervalId)
}, [])

CodePudding user response:

Quick fix

  const [val, setval] = useState(0)
  // const [isRunning, setIsRunning] = useState(true)
  // use useRef if you dont want to react to changes in useEffect
  const isRunningRef = useRef(true)

  useEffect(() => {
    let intervalId
    if (isRunning.current) {
      intervalId = setInterval(() => {
        if (val <= 100) {
          setval((t) => t   5)
        } else {
          console.log('call')
          isRunning.current = false;
          // stop the interval here if needed
          clearInterval(intervalId)
        }
        console.log('running') // This is being called continuously
      }, 50)
    } 

    return () => {
       if (intervalId !== undefined) {
          // clear if only timer was set
          clearInterval(intervalId)
       }
    }

  }, []) // userRef varaibles not needed in depedency array

React beta docs explain the problem, but solution is provided using useEvent which is in RFC, so useRef can be used till then

Hope it solves your problem

  • Related