I have below piece of code -
class Sum extends React.Component {
constructor(props) {
super(props)
this.state = { a : 0 }
}
// let's call this ADD-1
add = () => {
this.setState({ a: this.state.a + 1 })
this.setState({ a: this.state.a + 2 })
this.setState({ a: this.state.a + 3 })
}
render() {
return (<div>
<button onClick={this.add}> click me </button>
<div> sum {this.state.a} </div>
</div>)
}
}
this renders on clicking the button
sum = 3
where as i was hoping that it will render sum = 6
ie 1 + 2 + 3
also, if I change my add
method to something like to accommodate for prevState
race condition-
// let's call this ADD-2
add = () => {
this.setState({ a: this.state.a + 1 })
this.setState({ a: this.state.a + 2 })
this.setState(prevState => ({ a: prevState.a + 1 }))
this.setState(prevState => ({ a: prevState.a + 4 }))
}
it renders sum = 7
whereas I was hoping for sum = 8
ie (1 + 2 + 1 + 4)
Now two questions come to my mind:-
1) Why do we see the results as the one mentioned above and not what I have expected?
2) Why don't I see the transition of addition in UI? Say if we consider method tagged as ADD-1
, I should be seeing something like sum = 1
then sum = 3
then sum = 6
. Is it because of batching of updates but batching puts them in a queue of execution it doesn't override anything in my opinion.
"React may batch multiple setState() calls into a single update for performance."
From the Docs
Make sure you pass a function to setState
when your update is dependent on current state so it doesn't get overwritten by subsequent setState
.
Example
class App extends React.Component {
state = { a: 0 };
add = () => {
this.setState(previousState => {
return { a: previousState.a + 1 };
});
this.setState(previousState => {
return { a: previousState.a + 2 };
});
this.setState(previousState => {
return { a: previousState.a + 3 };
});
};
render() {
return (
<div>
<button onClick={this.add}> click me </button>
<div> sum {this.state.a} </div>
</div>
);
}
}
State update maybe asynchronous. Check this answer .
In an answer by Dan abramov, it is stated that state updates within one event call will only produce a single re-render at the end of the event.
no matter how many setState() calls in how many components you do inside a React event handler, they will produce only a single re-render at the end of the event.
And also batching happens only for state updates within a React event handler ie batching does not happen inside AJAX calls
promise.then(() => {
// We're not in an event handler, so these are flushed separately.
this.setState({a: true}); // Re-renders with {a: true, b: false }
this.setState({b: true}); // Re-renders with {a: true, b: true }
this.props.setParentState(); // Re-renders the parent
});
But you could achieve what you want to by passing a callback to the setState
method
add = () => {
this.setState({ a: this.state.a + 1 },
() => {
this.setState({ a: this.state.a + 2 },
() => {
this.setState({ a: this.state.a + 3 })
})
})
}
The above will return sum = 6.
When not to use callbacks in setState :
PureComponent
andshouldComponentUpdate
can be used to tune up a component's performance. They work by preventing lifecycle methods from firing when props and state haven't changed.The
setState
callback fires regardless of whatshouldComponentUpdate
returns. So, thesetState
callback will fire, even when state hasn't changed.
state change can happen only through setState, but if you access and modify this.state before setState (this.state.a + 1) it won't reflect. so every time you access this.state.a it's getting 0 ie initial value.
add = () => {
this.setState({ a: this.state.a + 1 }) // 0 + 1
this.setState({ a: this.state.a + 2 }) // 0 + 2
this.setState({ a: this.state.a + 3 }) // 0 + 3
}
this.setState((previousState) => { // change state using previousState} );
this is the way to have to update state
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.