0

PLEASE NOTE: This is not a duplicate of ReactJS - Need to click twice to set State and run function. The solution there does not work for me.

This is my initial state:

constructor(props) {
    super(props)
    this.state = {
        /* some code */
        game: { // game-level
            /* some code */
            /* value options: 'ready' 'in-progress', 'paused', 'cleared' */
            status: 'ready'
        },
    } /* END state */
} /* END constructor */

I am trying to change this.state.game.status to in-progress on button click, and once it is changed, I want to start a timer.

The button inside render():

<RaisedButton label="Start" primary={true}
    onClick={()=> this.changeGameStatus('in-progress')} />

The function that's called on button click:

changeGameStatus = (status) => {
    console.log('status = ' + status)
    this.setState({
        game: {
            status: status
        }
    })
    console.log('new status:' + this.state.game.status)

    this.startTimer()
}

The startTimer() function

startTimer() {
    if (this.state.game.status === 'in-progress') {
        console.log('The timer has started')
        this.timerID = setInterval(
          () => this.updateTimer(),
          1000
        )
    }
} /* END startTimer */

The problem is, this.state.game.status is not updated on first button click, and therefore, does not start the timer. I had to click the button twice for the whole thing to work, which is not very advisable.

NOTE:

There is an answer to the other question I mentioned above, but it specifies that I call the function inside componentWillUpdate(). It does not work for me, because it calls the startTimer() with every tick, thereby making the timer run twice as fast everytime.

How can I update my state and call the timer function with a single button click? I think this is simple, but I am new to ReactJS, so I have no idea how to do it at the moment. Thank you very much.

Community
  • 1
  • 1
ITWitch
  • 1,729
  • 5
  • 20
  • 38

1 Answers1

1

Use the callback approach for setState,since it takes some time to mutate the state and as JS is async , this.startTime() is executed even before the state has mutated and hence you need the second click which does the same thing but by this time the state is already changed and hence it works

changeGameStatus = (status) => {
    console.log('status = ' + status)
    this.setState({
        game: {
            status: status
        }
    }, () => {
         console.log('new status:' + this.state.game.status)

    this.startTimer()
    })

}
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • So that's what it's called... a `callback`. I've encountered the syntax several times already, but could not understand it before. Thank you very much! This solves my problem. – ITWitch Apr 17 '17 at 14:36
  • Yes its called a callback and you will need it whenever you are performing async actions on the updated state. – Shubham Khatri Apr 17 '17 at 14:37
  • 1
    Yes, I already did. I had to wait for the 15 minutes to be over. What a weird process, don't you think? I didn't know this existed until now: https://meta.stackoverflow.com/questions/250132/why-cant-i-accept-an-answer-in-the-first-15-minutes-after-asking-the-question – ITWitch Apr 17 '17 at 14:40
  • Maybe they did it because sometimes better answers can come within that time – Shubham Khatri Apr 17 '17 at 14:56