9

So I have some confusion regarding the async nature of setState in ReactJS. As per React docs, you shouldn't use this.state inside setState(). But if I have a counter as a state and i want to update it on click like this:

class App extends React.Component {
    state = { counter: 0 }

    onClick = () => {
        this.setState({counter: this.state.counter + 1})
    }

    render() {
        return (
          <div>
            <div>{this.state.counter}</div>
            <p onClick={this.onClick}>Click me</p>
          </div>
        )
    }
}

This works as expected. So why is this code wrong?

UPDATE: I know that setState is async, and it accepts a callback which has previous state as an argument, but I am not sure why I should use it here? I want to refer to the old state inside setState, so why should I use the callback function in this case? Whenever this.setState() is executed, this.state inside it will always refer to the old state, and its value will be changed to the new state only after setState has finished executing, not while it is executing.

1

2 Answers 2

22

You have access to prevState from within your setState call:

this.setState((prevState) => ({
    counter: prevState.counter +1
}))

That will allow you to safely increment the current state value.

The React documentation summarises why you cannot rely on this.state to be accurate during update: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

Sign up to request clarification or add additional context in comments.

6 Comments

But isn't this.state inside setState already referring to the previous state in my code? So why use the setState callback in this case?
@maverick Because React can and will batch setState calls, so there is no guarantee at what point that call will be fired. This means that it will read from this.state at potentially the wrong time. Using the callback is the safest way to access previous state from within the setState call.
I understand that setState is async. Which means that it won't get called immediately. But when it does get called, this.state will still be referring to the old state, which is what I want to access in this case. this.state will update its value after setState has finished execution, but not while it is executing.
The React docs summarise why you should not rely on this.state better than I can, they even have a counter example. Answer updated
Please, see the examples in the official documentation on: reactjs.org/docs/hooks-state.html where they use: <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> So I am also confused like @maverick.
|
2

setState accepts a function as a parameter with previous state as argument. Refacto as follows to avoid competition problems:

onClick = () => {
    this.setState(prevState => ({counter: prevState.counter + 1}))
}

2 Comments

What completion problems? Can you give an example? I know that setState accepts a callback, but I am not sure why it should be used in this case?
@maverick Since setState is async, you can't assume that this.state is the last value you updated. Take a look at this: stackoverflow.com/a/48209870/5735030.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.