Why is setInterval not being cleared in REACT the same way as it is in vanilla JS

I was creating a simple stopwatch component, when I found this weird behavior. I wanted a pause button to delete an interval, but it does nothing (the interval is still running after clicking on pause btn). It feels weird to me, because the same code was easily working in vanilla js and it would make the timer to stop counting.

  const [timer,setTimer] = useState(0)
  let interval;

  const tick = () =>{
    setTimer(prev=>prev+1)
  }

  const startInterval = () =>{
    interval = setInterval(tick,1000)
  }

  const pauseInterval = () =>{
    clearInterval(interval)
  }
  
  return (
    <div>
      <h1>Timer: {timer}</h1>
      <button onClick={startInterval}>start</button>
      <button onClick={pauseInterval}>pause</button>
    </div>
  )

The only way to go around this and make it work was to create another state, where I would keep my active interval.

const [timer,setTimer] = useState(0)
  const [activeInterval,setActiveInterval] = useState(null)
  let interval;

  const tick = () =>{
    setTimer(prev=>prev+1)
  }

  const startInterval = () =>{
    interval = setInterval(tick,1000)
    setActiveInterval(interval)
  }

  const pauseInterval = () =>{
    clearInterval(activeInterval)
    setActiveInterval(null)
  }
  
  return (
    <div>
      <h1>Timer: {timer}</h1>
      <button onClick={startInterval}>start</button>
      <button onClick={pauseInterval}>pause</button>
    </div>
  )

Why can’t I remove the interval as I tried to do in the first code example?

>Solution :

You have to hold a Reference to your interval using useRef.

Currently you are storing the key to the interval in a variable defined in the function body (let interval;), the problem with that is that after the component has been rerendered, let interval loses its value.

To preserve state between renders you can use useRef or useState.

const intervalContainer = useRef();

...

const startInterval = () => {
    intervalContainer.current = setInterval(tick,1000)
    setActiveInterval(interval)
}

const pauseInterval = () => {
    clearInterval(intervalContainer.current)
    setActiveInterval(null)
}

Leave a Reply