Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 816
user2668735 Avatar asked Jan 27 '17 14:01

user2668735


People also ask

Is setState is synchronous or asynchronous?

This function is used to update the state of a component, but it's important to remember that setState is asynchronous.

Can you use async await on setState?

However it is easy to forget that the setState method is asynchronous, causing tricky to debug issues in your code. The setState function also does not return a Promise. Using async/await or anything similar will not work.

Can we make setState synchronous?

according to the official documentation of react, we can make setState synchronous by passing the callback function in the second argument.

What is getState React?

getState()​Returns the current state tree of your application. It is equal to the last value returned by the store's reducer.


2 Answers

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

like image 118
inostia Avatar answered Sep 20 '22 14:09

inostia


It's actually not a bug/problem, but an architecture decision: the state is not intended to be used as a simple property/variable/storage, it's specifically meant to be used for interface/visual state and, as such, don't need to be updated at every call. It uses an internal queue, so if you swap the state many times before rendering, it will actually update it only once with the final value and by the time render method is invoked, it will contain the right value.

If you simply need to store/retrieve information during the execution or between methods that run in the same phase (componentWillReceiveProps and shouldComponentUpdate, for example), you can safely use this.anyProperty as always:

componentWillReceiveProps() {
  this.value = 'guaranteed';
  return true;
}
shouldComponentUpdate() {
  if (this.value === 'guaranteed') {
    console.log('will always return true');
  }
}
componentDidUpdate() {
  this.value = ''; //cleanup
}

In the example above, if you used "setState" there would be no guarantee that the value would always be updated in "shouldComponentUpdate", but it shouldn't matter if you use it for its intended purpose. The state changes are guaranteed to have been flushed by render time, so it should only contain information used in the rendering phase, not transactional/internal data for your objects. You are free to keep using object properties as usual.

like image 35
diego nunes Avatar answered Sep 20 '22 14:09

diego nunes