Starting with V16.3.0, the following three lifecycle hooks are marked UNSAFE.

  • componentWillMount

  • componentWillRecieveProps

  • componentWillUpdate

The reasons are as follows:

  • These three hooks are often used incorrectly, and there are better alternatives (the new getDerivedStateFromProps and getSnapshotBeforeUpdate).

  • React Moves from Legacy mode to Concurrent mode and these hooks behave differently.

This article will examine both of these points from the React source point of view.

React Is an asynchronous state update mechanism.

A misused hook

Let’s discuss the first point, we USES the example of componentWillRecieveProps here.

We often deal with props in componentWillRecieveProps the effects of change. Some students thought that this hook would fire after each props change.

Is that true? Let’s look at the source code.

This code comes from the updateClassInstance method:

if (
unresolvedOldProps ! == unresolvedNewProps ||oldContext ! == nextContext) {
  callComponentWillReceiveProps(
 workInProgress,  instance,  newProps,  nextContext,  ); } Copy the code

You can see the source code here

Will call componentWillRecieveProps callComponentWillReceiveProps method.

As you can see, the key is to compare whether unresolvedOldProps and unresolvedNewProps are congruent, and whether the context has changed.

UnresolvedOldProps is the props for the last update of the component, and unresolvedNewProps is the props parameter in the JSX returned by the ClassComponent call this.render.

So they’re quoting differently. So their congruent comparison is false.

Based on this reason, every parent component update componentWillRecieveProps trigger current component.

Have you ever misused it?

Schema migration

Let’s look at the second reason:

React Moves from Legacy mode to Concurrent mode and these hooks behave differently.

So what is a pattern? What are the differences between different models?

From the Legacy to the Concurrent

With so many changes to the source code following the upgrade from Act15 to Act16, it would be more accurate to say React was refactored.

Because of this change, some features behave differently in React between the old and new versions, including the three lifecycle hooks discussed above.

React offers three modes for developers to move smoothly from old versions to new ones:

  • Legacy mode– byReactDOM.renderThe created application will enable this mode. This is the currentReactThe way it is used. This mode may not support some new features.
  • Blocking mode– byReactDOM.createBlockingRootThe created application will enable this mode. Open the partconcurrentMode features as migrated toconcurrentThe first step of the pattern.
  • Concurrent mode– byReactDOM.createRootThe created application will enable this mode. Future-oriented development patterns.

You can see the feature support for different modes here

The main difference between concurrent mode and the Legacy mode we currently use is that the synchronous update mechanism is refactored into asynchronous interruptible updates.

Let’s take a look at how React implements asynchronous updates and why hooks behave differently with asynchronous updates than with synchronous updates.

Synchronous update

We can use code versioning as an analogy to update mechanisms.

Before we had code versioning, we gradually overlaid functionality in the code. Everything seemed in order until we hit an emergency line bug (red node).


To fix this bug, we need to commit the previous code first.

In React, all applications created with reactdom.render update their state in a similar way.

That is, all updates are executed synchronously, with no concept of priority, and new high-priority updates (red nodes) need to be executed after other updates.

Asynchronous update

When code version control is in place and there are emergent online bugs that need to be fixed, we temporarily store the changes in the current branch, fix the bugs in the master branch and rush online.


Git rebase is used to connect to the development branch. The development branch continues development based on bug fixes.


In the React by ReactDOM. CreateBlockingRoot and ReactDOM createRoot created in the task of the application of without expiration will adopt the way of asynchronous update the status.

High-priority updates (red nodes) interrupt ongoing low-priority updates (blue nodes) to complete the rendering process first.

After the high-priority update is completed, the low-priority update is updated based on the partial or complete results of the high-priority update.

Deep source

In the React source code, an Update object is created each time an Update is initiated, and multiple updates from the same component (A -> B -> C) are stored as A linked list in updateQueue.

Take a look at their data structure first.

Update has a number of fields, so for now we’ll focus on the following three:

const update: Update<*> = {
/ /... Omit fields that do not currently require attention  lane,
  payload: null,
  next: null
}; Copy the code

Update is returned by the createUpdate method, and you can see the createUpdate source code here

  • Lane: indicates the priority. In the figureredNodes andblueNode differences.
  • Payload: Updates mounted data. forthis.setStateTo create theupdate.payloadforthis.setStateThe refs.
  • Next: And othersUpdateJoin to form a linked list.

The updateQueue structure is as follows:

const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,
    firstBaseUpdate: null.    lastBaseUpdate: null.    shared: {
 pending: null. },  // Other parameters are omitted... }; Copy the code

UpdateQueue is returned by the initializeUpdateQueue method. You can see the initializeUpdateQueue source code here

  • baseState:updateBased on whichstateStart. In the figure aboveVersion controlIn the example of high quality bug fixes submitted aftermaster, othercommitBased on themasterThe branch continues to develop. Here,masterThe branch isbaseState.
  • firstBaseUpdatewithlastBaseUpdate:updateBased on whichUpdateStarted byfirstBaseUpdateStart tolastBaseUpdateEnd to form a linked list. theseUpdateIn the lastupdateDue topriorityNot enough to be left behind, as shown hereA B C.
  • shared.pending: Single or multiple updates for this updateUpdateA linked list.

BaseUpdate + shared.pending is the Update that needs to be executed.

example

Having learned about data structures, we then simulate an asynchronous interrupt update to reveal the secret of this article — Why componentWillXXX UNSAFE.

There are four updates in a component updateQueue. Letters represent the letter to be updated and numbers represent the priority of the Update. A smaller number indicates a higher priority.

baseState = ' ';

A1 - B2 - C1 - D2
Copy the code

Priority 1 when first rendered. The priority of B and D is not enough.

To ensure Update consistency, the first skipped Update (B) and all subsequent updates will be baseUpdates for the second rendering, regardless of their priority, which is B, C, and D.

baseState: ' '
Updates: [A1, C1]
Result state: 'AC'
Copy the code

Then render the second time, priority 2.

Since B was skipped in the first render, any render made by C after him will not be reflected in the second render baseState. So baseState is A instead of the last render Result state AC. This is also to ensure that updates are consistent.

baseState: 'A'          
Updates: [B2, C1, D2]  
Result state: 'ABCD'
Copy the code

We see that C appears in Updates of two renders at the same time, and the state he represents is updated twice.

If there is similar code:

componentWillReceiveProps(nextProps) {
    if (!this.props.includes('C') && nextProps.includes('C')) {
        / /... do something
    }
}
Copy the code

React may be called twice, which is not consistent with synchronizing updates on React.

For these reasons, componentWillXXX is marked UNSAFE.

conclusion

Due to space constraints, we’ve only scratched the surface of the React source code.

If you want to learn more about the React source code, here is the open source, rigorous and easy to understand ebook on React Technology