Problem with clearInterval in React

Asked

Viewed 57 times

1

I’m having a problem with React trying to use clearInterval. He’s not stopping the break created by setInterval.

Can you give me a hand and see what I’m missing or what I’m missing, please?

function App() {
  let [count, setCount] = useState(0)

  function showTimer() {
    setCount(count++)
  }

  function startTimer() {
    setInterval(showTimer, 1000)
  }

  function pauseTimer() {
    clearInterval(startTimer)
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={startTimer}>start</button>
      <button onClick={pauseTimer}>pause</button>
    </div>
  )
}

export default App;
  • 3

    When you call setInterval, he returns an ID that identifies the intermission created. It is this ID that you need to pass to the function clearInterval. Currently, you are passing a function that has nothing to do with what clearInterval wait. You can use a reference (hook useRef) to keep and update this ID within your component.

2 answers

3

As @Luiz Felipe said in the comment, you are running the clearInterval in its function, and not in the range itself. This is one of the points that needs to be corrected and we can do this with the hook useRef.

Another point that needs to be corrected is that as this code is asynchronous, we should take extra care with the useState and make a small adaptation to get the correct value of count. You can read more about it in that reply.

Then the code will look like this:

function App() {

  const [count, setCount] = React.useState(0);
  const timerRef = React.useRef();

  function showTimer() {
    setCount(oldCount => (oldCount + 1));
  }

  function startTimer() {
    timerRef.current = setInterval(showTimer, 1000);
  }

  function pauseTimer() {
    clearInterval(timerRef.current);
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={startTimer}>start</button>
      <button onClick={pauseTimer}>pause</button>
    </div>
  )
}

ReactDOM.render(<App />, document.querySelector('#app'));
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>


Another way to create this counter is by using the useEffect, but still using the useState and useRef. You can read this article from Medium to see how it would look.

  • Thank you, you helped me a lot! I’m just trying to understand the oldCount. In case it would be a callback function?

  • @rmoraes more or less. A answer that I Linkei explains well about it, and also has the documentation. What happens is that when you pass a function to the setState, you must still return the new value, but take the old value as parameter. Read the documentation to better understand which cases this may be important.

3

Failed to manage which variable is storing the interval (the setInterval returns a value that is the created range and this value is used to stop this timer) and with the programming using the Hook useRef can solve the problem, in your code have some problems also that are for example at the time of assigning the new value to the counter and also the interval as already reported, a basic example of solution:

function App() {
  const [count, setCount] = React.useState(0)
  const intervalRef = React.useRef(null);

  function showTimer() {
    setCount(state => state + 1); // ou setCount(state + 1);
  }

  function startTimer() {
    intervalRef.current = setInterval(showTimer, 1000);
  }

  function pauseTimer() {
    clearInterval(intervalRef.current);    
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={startTimer}>
        start
      </button>
      <button onClick={pauseTimer}>
        pause
      </button>
    </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root">Carregando ...</div>

Another example with some screen improvements, making a toggle with the buttons to visually know what is happening in the component:

function App() {
  const [count, setCount] = React.useState(0)
  const [time, setTime] = React.useState(null);

  const showTimer = React.useCallback(() => {
    setCount(state => state + 1); // ou setCount(state + 1);
  })

  const startTimer = React.useCallback(() => {
    setTime(setInterval(showTimer, 1000));
  })

  const pauseTimer = React.useCallback(() => {
    clearInterval(time);    
    setTime(null);
  })

  return (
    <div>
      <p>{count}</p>
      <button onClick={startTimer} disabled={time !== null}>
        start
      </button>
      <button onClick={pauseTimer} disabled={time === null}>
        pause
      </button>
    </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="root">Carregando ...</div>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.