ReactJS onClick state change one step behind

10,182

Solution 1

setState() is not necessarily a synchronous operation:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state aft

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

For this reason, this.state.questionNumber may still hold the previous value here:

this.props.changeHeader("Question " + this.state.questionNumber)

Instead, use the callback function that is called once the state transition is complete:

this.setState({
    questionNumber: this.state.questionNumber + 1
}, () => {
    this.props.changeHeader("Question " + this.state.questionNumber)
})

Solution 2

As Sandwichz says, if you access the state right after using setState, you have no guarantee of the actual value. You could do something like this:

handleContinue() {
  if (this.state.questionNumber > 3) {
    this.props.unMount()
  } else {
    const newQuestionNumber = this.state.questionNumber + 1
    this.setState({
      questionNumber: newQuestionNumber
    })
    this.props.changeHeader("Question " + newQuestionNumber)
  }
}
Share:
10,182
Jackson Lenhart
Author by

Jackson Lenhart

Musician interested in coding.

Updated on June 04, 2022

Comments

  • Jackson Lenhart
    Jackson Lenhart almost 2 years

    I'm building a very primitive quiz app with ReactJS and I'm having trouble updating the state of my Questions component. Its behavior is it renders the correct index of the questions array to the DOM despite this.state.questionNumber always being one step behind in handleContinue():

    import React from "react"
    
    export default class Questions extends React.Component {
      constructor() {
        super()
        this.state = {
          questionNumber: 1
        }
      }
    
      //when Continue button is clicked
      handleContinue() {
        if (this.state.questionNumber > 3) {
          this.props.unMount()
        } else {
          this.setState({
            questionNumber: this.state.questionNumber + 1
          })
          this.props.changeHeader("Question " + this.state.questionNumber)
        }
      }
    
      render() {
        const questions = ["blargh?", "blah blah blah?", "how many dogs?"]
        return (
          <div class="container-fluid text-center">
            <h1>{questions[this.state.questionNumber - 1]}</h1>
            <button type="button" class="btn btn-primary" onClick={this.handleContinue.bind(this)}>Continue</button>
          </div>
        )
      }
    }
    
  • Jackson Lenhart
    Jackson Lenhart over 7 years
    I really like this callback function solution
  • foyss
    foyss over 4 years
    The callback function is a godsend