In the last article we gave an example, but only said React. CreateElement, leaving reactdom.render. ReactDOM. Render is so loaded that it must be disassembled.

ReactDOM. Render can be divided into three parts according to function and call order: schedule, reconciliation, and render.

Here’s a quick rundown of what they do:

  • Scheduler: Indicates the priority of the task that is scheduled. The high task moves to the Reconciliation phase
  • Reconciliation: Identifies the components that have changed
  • Render: Render the changed component to the page

Because there are so many things to do, the call stack of Reactdom. render is very deep, and it is easy to make a loop by directly looking at the code one by one, so I found an interface generated by React and recorded it using Chrome’s Performance. You can see its call stack as follows:

The three boxes in the picture above basically represent three functional blocks.

We’re not going to follow the order in which the three function blocks are called, because it might be difficult to learn the React source code that way, so I’m going to try to figure out what Fiber is, which is something that runs through.

What is the Fiber

Fiber is a special data structure (a special linked list with parents and brothers) and an implementation of a virtual DOM that supports tasks with different priorities, can be interrupted and recovered, and can reuse the previous intermediate state when recovered.

A ReactElement corresponds to a Fiber object.

The source code

Fiber source in the packages/react – the reconciler/SRC/ReactFiber. New. Js directory

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Properties as static data structures
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Connect other Fiber nodes to form a Fiber tree
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  // As a dynamic unit of work attribute
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  // Scheduling priority is related
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  // Point to the corresponding fiber from the next update
  this.alternate = null;

  // Omit the following code
}
Copy the code

Explain a few of the more important attributes:

  • Tag A tag of the current node type. For example, the root node tag is 3. The function component tag is 0.ReactWorkTags.jsWell defined
  • Key is the key property
  • stateNode FiberThe corresponding real DOM node
  • ElementType is a tag for a real DOM element, such as h3, div, and so on
  • Return, child and sibling are respectively the parent, son and brotherfibernode
  • Several effects-related markers that record whether changes are needed,ReactFiberFlags.jsThere is a complete definition
  • Lanes are used in the scheduling phase
  • Alternate is from the last renderingfiber

Double cache

What is dual caching

When we draw an animation on Canvas, we call ctx.clearRect to clear the previous frame before each frame is drawn.

If the calculation amount of the current frame is large, there will be a long gap between the clearance of the previous frame and the drawing of the current frame, and a white screen will appear.

To solve this problem, we can draw the animation of the current frame in memory, and directly replace the previous frame with the current frame after the drawing is finished. Because the calculation time between the replacement of two frames is saved, there will be no flicker from the white screen to the screen.

This technique of building in memory and replacing directly is called dual caching.

Double cache in React

React uses a “dual cache” to build and replace the Fiber tree — which corresponds to creating and updating the DOM tree.

There are at most two Fiber trees in React. The Fiber tree currently displayed on the screen is called the Current Fiber tree, and the Fiber tree being built in memory is called the workInProgress Fiber tree. They are connected through alternate properties.

Root Fiber

React creates a FiberRootNode and rootFiber when it first loads the interface.

FiberRootNode is the root node of the entire container, which has only one.

RootFiber is the root node of the component tree. It is also a FiberNode. There can be multiple component trees, and different component trees have different root nodes.

The FiberRootNode current property points to the Fiber tree corresponding to the rendered content on the current page, the Current Fiber tree.

At the time of first rendering, no DOM had been mounted on the page, so the FiberRootNode’s current property was empty and the Current Fiber tree was empty.

Next, enter the Reconciliation phase of the Render phase, and according to the JSX returned by components, create Fiber nodes in memory in turn and connect them together to build a Fiber tree, which is called the workInProgress Fiber tree.

During the construction of the workInProgress Fiber tree, the properties within the existing Fiber nodes in the current Fiber tree are tried to be multiplexed, and only the corresponding current Fiber (i.e. Rootfiber.alternate) exists in the first screen rendering.

Below is FiberRootNode source, path of packages/react – the reconciler/SRC/ReactFiberRoot. New. Js

function FiberRootNode(containerInfo, tag, hydrate) {
  this.tag = tag;
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.current = null;
  this.pingCache = null;
  this.finishedWork = null;
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.callbackNode = null;
  this.callbackPriority = NoLane;
  this.eventTimes = createLaneMap(NoLanes);
  this.expirationTimes = createLaneMap(NoTimestamp);

  this.pendingLanes = NoLanes;
  this.suspendedLanes = NoLanes;
  this.pingedLanes = NoLanes;
  this.expiredLanes = NoLanes;
  this.mutableReadLanes = NoLanes;
  this.finishedLanes = NoLanes;

  this.entangledLanes = NoLanes;
  this.entanglements = createLaneMap(NoLanes);

  // Omit part of the code
}
Copy the code

The createHostRootFiber function helps initialize the current property.

The instance

import React from 'react';
import ReactDOM from 'react-dom';

function Com() {
  return (
    <div style={{ color: 'red' }}>
      <h3>Hello, Eagle!</h3>
    </div>
  )
}

ReactDOM.render(
  <Com />.document.getElementById('root'));Copy the code

Here is a demonstration of some of the FiberNode properties for div:

alternate: null
child: FiberNode {
    alternate: null
    child: null
    childLanes: 0
    dependencies: null
    elementType: "h3"
    firstEffect: null
    flags: 0
    index: 0
    key: null
    lanes: 1
    lastEffect: null
    memoizedProps: null
    memoizedState: null
    mode: 8
    nextEffect: null
    pendingProps: {children: "Hello, eagle!"}
    ref: null
    return: FiberNode {tag: 5.key: null.elementType: "div".type: "div".stateNode: null,... }selfBaseDuration: 0
    sibling: null
    stateNode: null
    tag: 5
    type: "h3"
    updateQueue: null
}
childLanes0
dependenciesnull
elementType"div"
firstEffectnull
flags2
index0
keynull
lanes0
lastEffectnull
memoizedPropsnull
memoizedStatenull
mode8
nextEffectnull
pendingProps: {
    children: {$$typeof: Symbol(react.element), type: "h3".key: null.ref: null.props: {... },... }style: {color: "red"}}refnull
return: FiberNode {
    alternate: FiberNode {tag: 3.key: null.elementType: null.type: null.stateNode: FiberRootNode,... }child: FiberNode {tag: 5.key: null.elementType: "div".type: "div".stateNode: null,... }childLanes: 0
    dependencies: null
    elementType: null
    firstEffect: null
    flags: 0
    index: 0
    key: null
    lanes: 0
    lastEffect: null
    mode: 8
    nextEffect: null
    pendingProps: null
    ref: null
    return: null
    sibling: null
	stateNode: FiberRootNode {tag: 0.containerInfo: div#root, pendingChildren: null.current: FiberNode, pingCache: null,... }tag: 3
	type: null
	updateQueue: {baseState: {... },firstBaseUpdate: null.lastBaseUpdate: null.shared: {... },effects: null}}siblingnull
stateNodenull
tag5
type"div"
updateQueuenull
Copy the code

Here are a few things you can see from this object:

  1. The currentfiberreturnProperty is its parent node, which is also the root node of the component, and alsoFiberNodeRootcurrentProperties;
  2. The currentfiberOf the parent nodestateNodeAttributes correspond toFiberNodeRootYou can see hiscontainerInfoIs the root node of our real DOM;
  3. The currentfiberpendingPropsProperties contain the properties of the current node;
  4. The currentfiberchildThe property is its child node, which is oursh3Node.