In the event

As you probably already know, in React compositing events, updates are processed in batches.

 handleClick() {
    console.log('Initially, a is'. + this.state.a);
    this.setState({a: 2});
    console.log(this.state.a);
    this.setState({a: 3});
    console.log(this.state.a);
}
Copy the code

In the React synthesized event, these callbacks are wrapped with a function called batchedUpdates.

This function will vary depending on the environment, but in the ReactDOM, it’s set here. The real implementation comes from the React-Reconciler package.

Here is an implementation of batchedUpdates:

export function batchedUpdates<A.R> (fn: A => R, a: A) :R {
  const prevExecutionContext = executionContext;
  
  // Set the current execution context to BatchedContext and then exit early when scheduling
  executionContext |= BatchedContext;
  try {
    // Fn is our callback function
    // For example, 'handleClick' in the example above
    return fn(a);
  } finally {
    // Set the context before the meeting
    executionContext = prevExecutionContext;
      
    // Start the update again after the end of the update, to achieve the effect of batch updateflushSyncCallbacksOnlyInLegacyMode(); }}Copy the code
  1. Concrete implementation of batchUpdates

In React, async functions written in the event callback function are not processed in batches. Instead, async functions are stored in the try… The finally statement is executed after the batch update, which is no longer in the context of the batch update.

 handleClick() {
    console.log('Initially, a is'. + this.state.a);
    setTimeout(() = > {
      this.setState({ a: 'settimout' })
      console.log(this.state.a);
    })
    new Promise((res) = > res()).then(res= > {
      this.setState({ a: 'promise' })
      console.log(this.state.a); })}Copy the code

The following result shows that the data is modified synchronously.

We can also see that binding native events in React does not generate batch updates because our own bound native events are not wrapped by the batchedUpdates function. If you also want batch updates in native events, you can add them manually:

import { unstable_batchedUpdates } from 'react-dom';

unstable_batchedUpdates(() = > {
    this.setState({
        a: 10
    })
     this.setState({
        a: 11})})Copy the code

Life cycle

componentDidMount() {
    this.setState({
      a: 2
    })
    console.log(this.state.a)
    this.setState({
      a: 3
    })
    console.log(this.state.a);
}
Copy the code

As you can see, it is asynchronous in the lifetime, and neighboring SET states are merged into one. Why is that?

React rendering is divided into render and commit phases. Render renders new changes in the Fiber tree, and commit phases apply the changes to the DOM tree, such as mounting a new DOM node to the page. Delete a DOM node.

ComponentDidMount is executed in commit mode. Also, during the COMMIT phase, React marks the current execution context as CommitContext. In this context, every time the component calls setState to initiate an update, it simply adds the update to the task queue, but exits before the update is actually scheduled.

When all the current tasks that need to be submitted to the DOM are executed, React dispatches the remaining update tasks again.

  1. The source location of setState
  2. ComponentDidMount calls the location
  3. After the COMMIT phase is complete, the tasks added to the queue are executed

Concurrent Mode

If Concurrent Mode is enabled, the scheduling initiated by setState will go through react-sceduler. The basis of this scheduling process is the implementation of asynchronous functions. Therefore, setState is asynchronous regardless of circumstances. Of course, don’t call flushSync directly.

function handleClick(){
    flushSync(() = > {
            setCount((count) = > count + 1);
    });

    flushSync(() = > {
            setCount(12);
    });
}
Copy the code