reactjsasynchronoussetstategetstate

React setState/getState and asynchronous


Why there is no async getState function in React ?

Documentation tel us that setState is async. Fine, but that means we can't safely use this.state and we need an async getState as well to respect execution order.

From what I understand we should never use this.state and use a getState function like this :

  getState(callback) {
    this.setState((prevState) => {
      callback(prevState) ;
    });
  }
  ...
  this.getState((curState) => {
    // we now can use the current state safely
  }

Anything that I am missing here in my way of thinking ? Why no such function exists in React ?

-- EDIT --

As a friend of mine told me it was not clear and since I am not convinced but the first answer, let's analyze some piece of code :

simpleFunc() {
    setState({ "counter" : 1 });
    setState({ "counter" : 2 });
    this.state.counter // => no garanty about the value
    getState((curState) => {  // ensure curState.counter is 2 });
}

This simple example shows that we can't use this.state directly in all situations since setState is async.

Here is a counter example where getState could be use : http://codepen.io/Epithor/pen/ZLavWR?editors=0010#0

Short answer: bad pratice, even not sure getState give us the current

The workaround is easy, but the fact that we can factorize some functions and use them without care about the context seems to be interesting, doesn't it ?

So, when many events occurs in a particular order, some events change the state, some read the state : how you can be sure, when an event read the state with this.state to read the good state since all changed are async ?

In fact all is about time :

T     : event 1, change state
T+1ms : event 2, change state
T+2ms : event 3, read state
T+3ms : event 4, change state

As you can't predict when exactly will occurs the setState of event 1 or 2, how you could guarantee that event 3 will really read the state set at event 2 ?

Short answer: events are queued in JS stack whereas state changes are queued in internal React queue. Internal React queue is fully unstacked before giving the hand.


Solution

  • You can definitely use this.state directly in general. You should never mutate state directly (this.state.foo = 0), and instead use setState whenever you want to mutate state.

    Usually a setState looks like this:

    this.setState({
        foo: 0
    })
    

    Then you can safely use this.state.foo eg in your render() function.

    There is a caveat however, in that due to the asynchronous nature of setState, you have no guarantee you will have immediate access to this.state after setState has been called.

    myFunc(baz) {
        this.setState({
            foo: baz + 1
        })
        console.log(this.state.foo) // not guaranteed
    }
    

    Better to do

    myFunc(baz) {
        const bazOne = baz + 1
        this.setState({
            foo: bazOne
        })
        console.log(bazOne)
    }
    

    Or use the setState functions second parameter, used as a callback executed when the setState operation is finished. In that callback you will have access to the updated state, i.e. this.state:

    myFunc(baz) {
        this.setState({ foo: baz + 1 }, () => {
            console.log(this.state.foo) // guaranteed in callback
        });
    }
    

    See: https://facebook.github.io/react/docs/react-component.html#setstate