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