Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setInterval with setState in React

I have a timer using setInterval() in a React component and I'm unsure what the best practices are in order to start and stop this interval in respect to using state. I'm running into some asynchronous issues with that.

Let's say I have a set of links in my React component that render and execute the callback fine:

let links = [10, 50, 100, 500, 1000].map((num) => {
  return(
    <Link key={num} onClick={(e) => this.switchNums(num)} to={`/somePath/${num}`}>{num}</Link>
  )
})

Here's the switchNums() function, where i want it to reset an existing timer:

switchNums(num){
  this.stopTimer()
  this.reset(num)
}

Here's startTimer(), stopTimer() and reset():

startTimer(){
  if(!this.state.timerId){      
    let timerId = setInterval(()=>{
      let timer = this.state.timer + 1
      this.setState({
        timer: timer,
        timerId: timerId
      })
    }, 1000)
  }
}

stopTimer(){
  clearInterval(this.state.timerId)     
  this.setState({timerId:null})
}

reset(size){
  this.setState({
    gameOver: false,
    counter: 0,
    correct: 0,
    numbers: this.getRandomNumbers(size),
    timer: 0
  }, this.startTimer())
}

One of the bugs is clicking on the links rapidly will cause multiple intervals to fire despite the if condition in startTimer(). I'm guessing this has to do with the asynchronous nature of setState(). Another bug (and I think related) is that when i click slowly, it only starts the interval every other time.

Can anyone shed some light on this? Or what they've done to circumvent asynchronous issues with setState being used in conjunction with setInterval(any way set state can return a promise?), Or which lifecycle methods would be best for this type of situation?

like image 1000
Andrew Kim Avatar asked Nov 09 '16 15:11

Andrew Kim


People also ask

Can we use setInterval in React?

Using setInterval lets you execute a function at specific intervals. It's often very useful in React apps, for example for checking a condition regularly or fetching data every so often.

How do you set setInterval in React?

Run setInterval() from a React button onClick event To stop the interval with a button click, you need to save the interval ID returned by the setInterval() method as a state value. Next, modify the handleClick() function and add an if block that checks for the intervalId value first.

How do you use setInterval in React useEffect?

A function or block of code that is bound to an interval executes until it is stopped. To stop an interval, you can use the clearInterval() method. ... useEffect(() => { const interval = setInterval(() => { setSeconds(seconds => seconds + 1); }, 1000); return () => clearInterval(interval); }, []); ...

How to use setInterval function in react?

We can use the setInterval function in React, just like how we can use in JavaScript. In this below example, we using the setInterval function inside useEffect hook. The useEffect hook runs the callback function when a component mounts to the dom, which is similar like componentDidMount life cycle method in class components.

When to use the setState method in ReactJS?

This method is really useful when we are setting a value in the state in such a way that it depends on its previous value. For example, toggling a boolean (true/false) or incrementing/decrementing a number. Click me! How to update an object with setState in ReactJS?

How do I increment a previous state property value in react?

Here’s a pattern for using the setInterval () method: You can call the this.setState () method inside the setInterval () method to increment a previous state property value. The following example uses the state property count value as a timer that counts how many seconds have passed since the component has been rendered on the screen:

How to change the state of a React component?

Whenever the state changes, React re-renders the component to the browser. Before updating the value of the state, we need to build an initial state setup. Once we are done with it, we use the setState () method to change the state object. It ensures that the component has been updated and calls for re-rendering of the component.


2 Answers

I think the biggest flaw here is that you're using state to store your interval. While technically possible, I see no reason why you would actually want to do that.

Instead, just use a local variable to your component:

startTimer(){
  if(!this.timerId){     
    this.timerId = setInterval(()=>{
      //your function
    }, 1000);
  }
}

stopTimer(){
  clearInterval(this.timerId);
}

So I don't think you need to use the state at all here for your timer. You have some other general questions in your post though that are related to state, and I'll try to answer those below. Just bear in mind that they are irrelevant in solving your particular issue.


What have they've done to circumvent asynchronous issues with setState()?

You can use a callback to execute code after the state has been set. There's a section of the official docs about this; here's what it says:

The second parameter is an optional callback function that will be executed once setState is completed and the component is re-rendered.

setState(nextState, callback);

Which lifecycle methods would be best for this type of situation?

The same section of the doc as above continues:

Generally we recommend using componentDidUpdate() for such logic instead.

If you have multiple setState in your function, and you want to execute specific code after a specific event, I think you're fine using the callback. For more general purposes use the life-cycle method above.

like image 119
Chris Avatar answered Sep 21 '22 21:09

Chris


Using React Hooks useState and useEffect you can do the following:

const [timer, setTimer] = useState(1);

useEffect(() => {
  const timerId = setInterval(() => setTimer(timer + 1), 1000);

  return () => clearInterval(timerId);
});
like image 44
oldo.nicho Avatar answered Sep 19 '22 21:09

oldo.nicho