1. SetState is synchronous or asynchronous

To answer this question, let’s look at an example,

import React, { Component } from 'react'
import './App.css';

class ClsApp extends Component {
    state = {
        num: 0
    }
    updateNum = () = > {
        console.log('before the setState.this.state.num)
        this.setState({ num: this.state.num + 1 })
        console.log('After setState -- normal'.this.state)

        // setTimeout(() => {
        // this.setState({ num: this.state.num + 1 })
        // console.log('setState -- setTimeout',this.state)
        // })
    }

    render() {
        return (
            <>
                <button onClick={this.updateNum}>+ 1</button>
                <p>{this.state.num}</p>
            </>)}}export default ClsApp

Copy the code

In this case, if the update is triggered by a normal click, the value of this.state is consistent and we say it is asynchronous, whereas if it is placed in setTimeout, it is inconsistent and we say it is synchronous.

It is meaningless to consider whether setState is synchronous or asynchronous

  • Going back to the above example, whether synchronous or asynchronous, we can get the updated result we want in the view, and when we need to do something that depends on the updated value, we should not write it after setState itself.
  • If we want to rely on the updated status
    • In ClassComponent, we can do this in componentDidMounted or componentDidUpdate
    • In FunctionComponent, we can execute in the useEffect callback
  • If you need to consider this feature in a service, it is most likely to be written in a wrong way. It is recommended that you re-code it in a proper way, rather than focusing on its synchronous asynchronous feature.

React Handles updates triggered by setState differently in different modes

Legacy mode — reactdom.render

  • UseSate is asynchronous by default because React has one inside itPerformance optimization mechanism - batchedUpdates batch processing
    • This performance optimization mechanism is designed to combine multiple setState calls to trigger updates into one update and reduce performance problems caused by excessive page rendering by the status update engine.
    • When we call setState in the react declare cycle function, we’ll execute batchedUpdates. We’ll add a batchedContext flag. Having this flag causes the setState action to be placed in the batchedUpdateQueue
    • Therefore, instead of performing updates immediately after calling setState, (1-n) useState actions are placed in the batchedUpdateQueue queue. When the React context ends, the batched flag is removed and the response callback is executed synchronously.
  • When we wrap setState with setTimeout, when we execute it, it doesn’t go inside reactBatchedUpdates optimization mechanism, the running context isnoContext; During scheduling updates, if yesSynchronization priority + noContext context, the synchronous execution callback queue will be executedflushSyncCallbackQueue.
  • So in Legacy mode, setState is executed asynchronously if it is used in a position where react is triggered, and synchronously if setTimeout is used in a position where react is not triggered.

Concurrent mode — experimental for now, reactdom.unstable_createroot ().render()

  • In Legacy, setState wrapped with setTimeout is executed synchronously because FiberRoot’s lane is Synclane synchronization priority in Legacy mode
    • When the priority is synchronized, if the context is noContext, synchronization is performed
  • In Concurrent mode, the priority is asynchronous, so all updates are asynchronous
  • So no matter how you handle it, updates triggered with setState are asynchronous.

summary

  • In Legacy mode, batchUpdates are asynchronously hit, and updates are synchronously missed
  • Both are asynchronous in Concurrent mode

reference

  • You can refer to the WorkLoop source filescheduleUpdateOnFiberFunction for further study
  • Teacher Karshon’s video on setState