In the first two articles, we analyzed the React component’s implementation, mounting, and lifecycle flow. While reading the source code, we often see code such as Transaction and UpdateQueue, which refer to two concepts in React: transactions and update queues. Since we’ve covered all of this in a flash in previous articles, we’ll explore transaction mechanisms and update queues based on the familiar setState method.

1. SetState related

As we learned in the first article, React Source Parsing (part 1: Component Implementation and Mounting), component prototypes declared via class have setState methods:

The method passes two arguments, partialState, which is the new state value, and callBack, which is the callBack function. The updater is defined in the constructor:

You can see that the updater is passed in by the constructor, so find where the new ReactComponent is executed and you can find out what the updater is. ReactCompositeComponent custom components, for example, in _constructComponentWithoutOwner approach, we found the trail of the it:

return new Component(publicProps, publicContext, updateQueue);
Copy the code

The updater parameter is actually updateQueue. Let’s look at this. Updater. What is the enqueueSetState enqueueSetState:

GetInternalInstanceReadyForUpdate method is to obtain the current component object, the purpose of the assignment to internalInstance variables. Next, determine whether the state update queue of the current component object exists. If so, add the partialState, that is, the new state value, to the queue. If not, create an update queue for the object, noting that the queue exists as an array. Let’s take a look at what the last call to enqueueUpdate does:

By the code, when batchingStrategy. IsBatchingUpdates to false, will perform batchedUpdates update queue, if it is true, will be in dirtyComponent components. Let’s look at the source code for batchingStrategy:

For a rough look, isBatchingUpdates starts with false, and batchedUpdates internally executes the incoming callback function.

It’s a bit confusing to see this logic, but from the code we get the vague idea that React doesn’t just update components randomly, but instead performs them based on status (like true/false). In fact, React internally uses the concept of a “state machine,” where components perform different logic when they are in different states. Take the component update process as an example. React updates components in the form of transaction + state, so let’s discuss the mechanism of transaction next.

2. The transaction affairs

First take a look at the parse diagram of the official source code:

<pre> * wrappers (injected at creation time) * + + * | | * +-----------------|--------|--------------+ * | v | | * | +---------------+ | | * | +--| wrapper1 |---|----+ | * | | +---------------+ v | | * | | +-------------+ | | * | | +----| wrapper2 |--------+ | * | | | +-------------+ | | | * | | | | | | * | v v v v | wrapper * | +---+ +---+ +---------+ +---+ +---+ | invariants * perform(anyMethod) | | | | | | | | | | | | maintained * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|--------> * | | | | | | | | | | | | * | | | | | | | | | | | | * | | | | | | | | | | | | * | +---+ +---+ +---------+ +---+ +---+ | * | initialize close | * + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + *</pre>
Copy the code

Each method is wrapped in a wrapper and must be called with perform. Perform initialize and close before and after the wrapped method. Here’s an example of the difference between a normal function and a function wrapped in a wrapper:

function method(){
    console.log('111')}; transaction.perform(method);// Execute the initialize method
/ / output '111'
// Execute the close method
Copy the code

We know that transaction.perform(callBack) actually calls Transaction.perform (enqueueUpdate) in the batchingStrategy code above, but t still exists in the enqueueUpdate method Ransaction.perform (enqueueUpdate)

To avoid the problem of possible infinite loops, wrappers come into play. Let’s see how these two wrappers are defined:

When transaction.perform(enqueueUpdate) is executed as a transaction, the process is as follows:

// Reset_batched_updates.initialize () is an empty function
// enqueue()
// RESET_BATCHED_UPDATES.close()
Copy the code

Use a flow chart to illustrate:

RESET_BATCHED_UPDATES The wrapper is used to set isBatchingUpdates, which is the status of the component to be updated, if the component needs to be updated, and then to revert to the original status after the update.

What good would it do? To avoid repeated render of components and improve performance, of course

RESET_BATCHED_UPDATES is used to change the Boolean value of isbatchedingUpdates to false or true. So what is the use of FLUSH_BATCHED_UPDATES? Flush_batched_updates.close () flush_batched_updates.close () flush_batched_updates.close ();

You can see that the flushBatchedUpdates method iterates through all of the dirtyComponents and invokes the runBatchedUpdates method transactionally. This method does two things:

  • One is through executionupdateComponentMethod to update the component
  • Second, ifsetStateMethod is passed a callback functioncallbackQueueThe queue.

Take a look at the updateComponent source:

Can see performed componentWillReceiveProps method and shouldComponentUpdate method. One thing we can’t ignore is that the _processPendingState method is executed before shouldComponentUpdate. Let’s take a look at what this function does:

If the update queue is null, return the original state; if the update queue is null, return the original state. 2. If there is an update in the update queue, return the update value. 3. If the update queue has multiple updates, merge them through a for loop; To sum up, during a lifecycle, before componentShouldUpdate is executed, all state changes are combined and processed uniformly.

Return to _updateComponent, and finally if shouldUpdate is true, execute the _performComponentUpdate method:

Roughly browse below will find the same routine, execute componentWillUpdate lifecycle method, after the completion of the update componentDidUpdate method. Let’s look at the _updateRenderedComponent method responsible for updating:

The idea of this code is clear:

  1. Get the old component information
  2. Get new component information
  3. shouldUpdateReactComponentIs a method (hereinafter referred to asshouldFunction () to determine whether to update based on incoming information about new and old components.
  4. shouldThe function returnstrueTo perform updates to older components.
  5. shouldThe function returnsfalseTo perform the unmount of the old component and the mount of the new component.

Combined with the previous flowchart, we supplement the entire component update process:

4. Put it at the end

(1)setStateThe callback function

The setState callback is handled by enqueueSetState, and the callback is handled by enqueueCallback. Interested readers can explore this for themselves.

(2) aboutsetStateThe resulting crash problem

We already know that this.setState actually calls enqueueSetState when the component is updated, because the new state has not been merged yet, So in performUpdateIfNecessary this._pendingStateQueue is true:

React will set this._pendingStateQueue to NULL, so that the next time a dirtyComponent is processed in batches, the updated component will not be repeated, ensuring that the component is updated only once.

The reason we can’t call setState at componentWillUpdate is that setState will set _pendingStateQueue to true, Causes updateComponent to be executed again, and then componentWillUpdate is called again, and finally componentWillUpdate is called in a loop, causing the browser to crash.

(3) React dependency injection

We in the previous code, to update the queue flag batchingStrategy, we directly to analyze ReactDefaultBatchingStrategy, this is because the React within the presence of large amounts of dependency injection. When React is initialized, reactDefaultinjection.js is injected into ReactUpdates as the default strategy. There are a number of applications for DEPENDENCY injection in React server rendering that interested students can explore.

Review: React Source Code Parsing (1): Component implementation and mounting “React Source Code Parsing (2): Component Life Cycle” React Source code Parsing (4): Event system contact email: [email protected]