SetState is asynchronous, right? What is the setState update process?

After contacting React, during the project development process, it was found that setState was an asynchronous method, and the real value could only be obtained in the callback function.

this.setState({
    num:this.state.num + 1= > {}, ()console.log(this.state.num);
});Copy the code

Why does react do this? Is setState really asynchronous?

Not so, according to relevant articles. Two other ways of doing setState synchronization.

// Method 1: After this.setState is called in componentDidUpdate, the this.state is updated
componentDidUpdate(){
    console.log(this.state.num)
}

In setTimeout, this.state is updated immediately after this.setState, so updated data can also be retrieved.
setTimeout((a)= >{
    this.setState({
        num:this.state.num + 1
    });
    console.log(this.state.num);
})Copy the code

How does setState update component state?

SetState The flow of updating a component

After calling setState, we push the state we want to update into a queue to be updated (that is, _pendingStateQueue for the internal instance), and then perform the push update operation enqueueUpdate to determine if we are in a batch update state. If a batch update is in progress, push the instance into dirtyComponents for the next batch update. Conversely, if no batch update is being performed, a batch update transaction is invoked.

/** * setState **/
 
ReactComponent.prototype.setState = function (partialState, callback) {
  this.updater.enqueueSetState(this, partialState); // Pass a new state into the queue
  if (callback) { // Push into the callback queue
    this.updater.enqueueCallback(this, callback, 'setState')
  }
}
 
enqueueSetState: function(publicInstance, partialState) {
  // Get an internal instance
  var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState')
  if(! internalInstance) {return
  }
 
  // Update the queue merge operation
  var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = [])
  queue.push(partialState)
 
  // Update the code
  enqueueUpdate(internalInstance)
}Copy the code

After setState pushes the partialState we want to update into the queue to be updated, it’s up to enqueueUpdate to handle the update timing. What does enqueueUpdate do for us?

/** * enqueueUpdate source code **/
 
function enqueueUpdate(component) {
  ensureInjected()
 
  // If not in batch update mode
  if(! batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component)return
  }
  // If in batch update mode, save the component in dirtyComponents
  dirtyComponents.push(component)
}Copy the code

You can see that there is an important object in enqueueUpdate, batchingStrategy, which has an attribute isBatchingUpdates that tells enqueueUpdate whether it should update or wait. Push the component into the dirtyComponents. Let’s take a closer look at react as an internal object that controls batch updates.

/** * batchingStrategy source code **/
 
var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false.batchedUpdate: function(callback, a, b, c, d, e) {
    var alreadyBatchingStrategy = ReactDefaultBatchingStrategy. isBatchingUpdates
    ReactDefaultBatchingStrategy. isBatchingUpdates = true
 
    if (alreadyBatchingStrategy) {
      callback(a, b, c, d, e)
    } else {
      transaction.perform(callback, null, a, b, c, d, e)
    }
  }
}Copy the code

The batchedUpdates provided in dirtyComponents are actually the method we’ve been looking for to actually update our component. React, however, introduces us to another big concept — transactions. Perform is the call to the transaction in batchedUpdates.

Affairs and componentDidMount

Now that we know how setState is executed, we also know that this function does not have to be executed asynchronously, and that it will trigger an update of the DOM immediately if it is not being updated.

componentDidMount () {
  this.setState({ val: this.state.val + 1 })
  console.log(this.state.val) / / 0
 
  this.setState({ val: this.state.val + 1 })
  console.log(this.state.val) / / 0
}Copy the code

When componentDidMount is executed, the dom rendering should be complete, so why can’t I call setState and update state as I did with setTimeout? A brief introduction to the transaction is thrown out.

The react method uses a Wrapper to wrap the method you want to call. Initialize and close the method you want to call. In the react method, the front hook is triggered first, and the end method is called after execution. This is used for exception handling in React.

ComponentDidMount is the end of a transaction that mounts a DOM node on react, where state is blocked and updates are not called again until the transaction is fully executed.

SetState and life cycle

State change = “update =” change state = “state change…… Which hook functions are safe to use setState in.

Let’s list the lifecycle hook functions

constructor -> 
componentWillMount -> 
render -> 
componentDidMount -> 
componentWillReceiveProps -> 
shouldComponentUpdate -> 
componentWillUpdate -> 
render -> 
componentDidUpdateCopy the code

Constructor itself has a declaration of state, in this case the initial state creation, so setState is not needed

A synchronous setState call for componentWillMount () does the same thing as the constructor definition of state and does not trigger additional rendering of the component.

Render, shouldComponentUpdate, componentWillUpdate these three functions, components have not finished rendering continue to call setState, will be infinite trigger update, into an infinite loop, is to pay attention to.

So the lifecycle hook functions we can use for setState are: ComponentWillMount, componentDidMount, componentWillReceiveProps (react16 obsolete, Use getDerivedStateFromProps to get props and return the new state) and componentDidUpdate

At this point, the principle and use of setState are introduced, but the real use opportunities are often the front-end developers need to consider, for non-control components, this is the necessary technical basis for react

Reference:

React setState Process parsing