Search code examples
javascriptreactjssetstatearrow-functions

Why does using an arrow function give the updated version of the state in React in comparison to calling the function normally?


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?


Solution

  • 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.