Take the following react code
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {state1: true, state2: false, state3: "hello"}
this.onClick = this.onClick.bind(this)
}
onClick() {
this.setState({state1: false, state2: true, state3: "goodbye"}, console.log(this.state))
}
render() {
return(
<button onClick = {this.onClick}></button>
)
}
}
ReactDOM.render(<Foo />, document.getElementById('root'));
When I use console.log
in the callback for setState
, I have to click the button twice in React in order for the console to display the correct version of state.
However, if write this.setState({state1: false, state2: true, state3: "goodbye"}, () => console.log(this.state))
instead, adding an arrow function, clicking the button once outputs the correct version of the state in the console. Why is that? Why does an arrow function output different behaviour in this scenario?
The expression console.log(this.state)
is first evaluated, and the resulting value is passed into setState
.
In other words, this
this.setState({state1: false, state2: true, state3: "goodbye"}, console.log(this.state))
can be re-written to the following without changing its behavior:
let consoleLogResult = console.log(this.state)
this.setState({state1: false, state2: true, state3: "goodbye"}, consoleLogResult)
You log the current state before you set it.
Now, console.log
always returns undefined
, so consoleLogResult
above will always be undefined
. It behaves like you never passed a callback 'function' at all.
If you instead pass () => console.log(this.state)
, it evaluates to a function, which is called by React after the state transition was executed.