After 2 years, I re-read the React source code, and now I understand a lot of things I didn’t understand before. This article will focus on the React Diff principle using a practical example and the relevant React source code. Use the latest React version: 16.13.1.

In addition, we will write a “Understand React source code series” this year, which will explain the core content of React in the most easy-to-understand way. React React

  • The React principle of the Diff

  • React Scheduling Principle

  • React source code – support breakpoint debugging for all versions

  • The React principle of Hooks

Welcome to Star and subscribe to my blog.

Before we discuss the Diff algorithm, it’s important to introduce React Fiber, because the React source code has various implementations based on Fiber, including the Diff algorithm. Of course, those familiar with React Fiber can skip Fiber.

Fiber profile

Fiber is not complicated, but it will take a while to fully understand it. The topic of this article is the Diff principle, so here is a brief introduction to Fiber.

Fiber is an abstract node object. Each object may have a child Fiber(child), an adjacent child Fiber(return), and a parent Fiber(return). React uses a linked list to connect all Fiber nodes to form a linked tree.

Fiber also has effectTags, such as replacement Placement and Deletion, for updating the DOM later.

foo

: {type: ‘div’, props: {children: ‘foo’}}

The Diff process

React source, about diff from reconcileChildren(…) To start.

The total process:

In the flowchart, the function names used in the source code are displayed, and complex parameters are omitted. “New content” is the new content being compared, which can be of one of three types:

  • Object: React element

  • String or number: text

  • Array: Array elements can be React elements or text

The new content is the React element

We first take the React element as an example to debug the code comprehensively. The methods that will be used repeatedly in the future are explained in this step, and a flow chart is used as a summary.

Case study:


function SingleElementDifferentTypeChildA() { return <h1>A</h1> }

function SingleElementDifferentTypeChildB() { return <h2>B</h2> }

function SingleElementDifferentType() {

 const [ showingA, setShowingA ] = useState( true ) 

 useEffect( (a)= > {

  setTimeout( (a)= > setShowingA( false ), 1000)})return showingA ? <SingleElementDifferentTypeChildA/> : <SingleElementDifferentTypeChildB/>

}

ReactDOM.render( <SingleElementDifferentType/>.document.getElementById('container'))Copy the code

From the first step reconcileChildren(…) Start debugging your code without focusing on diFF unrelated content, such as renderExpirationTime. You can see the corresponding variable type in the debug panel on the left.

Here:

  • WorkInProgress: parent Fiber

  • Current-. child: The old content in comparison corresponds to fiber

  • NextChildren: The new content being compared is the React element, which is of type object.

In Diff, the old content in the comparison is Fiber, while the new content in the comparison is the React element, text, or array. As you can see from this step, the React diff algorithm is quite different from the actual code.

Since the new contents are objects, the implementation of the reconcileSingleElement(…) continues. And placeSingleChild (…). .

Let’s first look at placeSingleChild(…) :

placeSingleChild(…) The function is simple: attach a Placement tag to the Differ Fiber, indicating that the DOM element corresponding to the old Fiber needs to be replaced later.

Continue to see reconcileSingleElement (…). :

This is where the diff(comparison) begins, with Child as the old content fiber and Element as the new content, each of which has a different element type.

Because of the type difference, React will “delete” the old content fiber and all its neighboring fibers (that is, add the side label Deletion to these fibers) and generate a new fiber based on the new content. Then set the new Fiber to the child of the parent Fiber.

At this point, a Diff process is complete where the new content is the React element and the old and new content have different element types.

What if the old content and the new content have the same element type?

Writing similar cases, we can get results

userFiber(…) :

userFiber(…) The main function is to clone a new content fiber based on the old content fiber and the properties of the new content (props), which is also called fiber multiplexing.

So when the old content and the new content have the same element classes, React will reuse the old content fiber and combine the new content properties to create a new fiber. Also, set the new fiber to the parent fiber’s child.

React element diff flow summary

The new content is text

When the new content is text, the logic is similar to when the new content is React element:

The new content is an array

Use cases:


function ArrayComponent() {

  const [ showingA, setShowingA ] = useState( true ) 

  useEffect( (a)= > {

   setTimeout( (a)= > setShowingA( false ), 1000)})return showingA ? <div><span>A</span><span>B</span>

  </div> : <div><span>C</span>

​    D

  </div>

}

ReactDOM.render( <ArrayComponent/>.document.getElementById('container'))Copy the code

If the new contents are arrays, they need to be reconcileChildrenArray(…). :

The for loop iterates through the new content array, pseudo-code (for understanding) :

for ( let i = 0, oldFiber; i < newArray.length; ) {... i++ oldFiber = oldFiber.sibling }Copy the code

When iterating through each new content array element:

updateSlot(…) :

Because newChild is of type object,

updateElement(…) :

updateElement(…) With reconcileSingleElement (…). Core logic consistency:

  • If the old and new content elements have the same type, clone the old fiber and combine the new content to generate a new fiber

  • If not, a new fiber is created based on the new content.

Similarly, updateTextNode (…). :

updateTextNode(…) With reconcileSingleTextNode (…). Core logic consistency:

  • If the label of the old content fiber is not HostText, a new fiber is created based on the text of the new content

  • If HostText is used, clone the old fiber and combine the new content text to generate the new fiber

In this case, the new content array for loop completes:

Because the length of the old and new content arrays is the same, the first new fiber is returned. React sets the new fiber to the child of the parent fiber.

React if the length of the array is different from the total number of fibers and its adjacent fibers.

Write similar cases.

If the new content array is shorter:

React will remove superfluous old content fiber adjacent to fiber.

If the new content array is longer:

React will iterate over the excess new content array elements, create a new fiber based on the new content array elements, and add the side effect tag Placement.

Summary of diff process when the new content is an array:

conclusion

React source code research diff algorithm, only debugging analysis of the relevant code, it is easier to get the answer.

Three cases of Diff:

  1. The new content is the React element
  2. The new content is text
  3. The new content is an array

In Diff, if the comparison results are the same, the old content Fiber is reused and the new content is combined to generate the new Fiber. If not, just create a new fiber with the new content.

Then add side effects replace labels to the old content Fiber, or side effects remove labels to the old content Fiber and all its adjacent elements.

Finally, set the new (first) fiber as the child of the parent fiber.

The resources

  • The How and Why on React’s usage of linked list in Fiber to walk The component’s tree: medium.com/react-in-de…

  • [translate] into the React fiber architecture and source: zhuanlan.zhihu.com/p/57346388

  • Inside Fiber: In-depth Overview of the New Reconciliation Algorithm in React: medium.com/react-in-de…

Thank you for taking the time to read this article. If you like this article, please like, bookmark and share, so that more people can see this article, which is also the biggest encouragement and support for me!

Welcome to wechat (scan the QR code below) orGithubSubscribe to my blog.