Photo credit: unsplash.com/photos/kLfk…

preface

Currently React has three modes:

  • legacyPattern: ReactDOM. Render (elementThe rootNode). This is how the React App is currently used. There are currently no plans to remove this schema, but it may not support these new features.
  • blockingPattern: ReactDOM createBlockingRoot (rootNode.) render (element). It is currently being tested. As the first step in migrating to concurrent mode.
  • concurrentPattern: ReactDOM createRoot (rootNode.) render (element). It is currently being tested and is intended to be the default development mode for React when it is stable in the future. This mode turns on all the new features.

The React of the render phase began in performSyncWorkOnRoot or performConcurrentWorkOnRoot method calls, it mainly depends on the updated is synchronous or asynchronous update.

performSyncWorkOnRoot & performConcurrentWorkOnRoot

  • performSyncWorkOnRoot

// performSyncWorkOnRoot calls this method

// The work loop is an extremely hot path. Tell Closure not to inline it.
/ * *@noinline * /
function workLoopSync() {
  // Already timed out, so perform work without checking if we need to yield.
  while(workInProgress ! = =null) { performUnitOfWork(workInProgress); }}Copy the code

You can see the source code here

  • performConcurrentWorkOnRoot

/ / performConcurrentWorkOnRoot will invoke this method

/ * *@noinline * /
function workLoopConcurrent() {
  // Perform work until Scheduler asks us to yield
  while(workInProgress ! = =null&&! shouldYield()) { performUnitOfWork(workInProgress); }}Copy the code

You can see the source code here

The only difference between the two modes is whether to call shouldYield. If the current browser frame has no time left, shouldYield stops the loop until the browser has idle time before continuing the loop.

WorkInProgress represents the currently created workInProgress Fiber.

The performUnitOfWork method creates the next Fiber node and assigns it to workInProgress, which is connected to the created Fiber node to form a Fiber tree.

React Fiber Reconciler is reconstructed from the Stack Reconciler and realizes interruptible recursion through traversal. Therefore, the work of performUnitOfWork can be divided into two parts: “recursion” and “return”.

The “pass” stage

The depth-first traversal starts from the root node rootFiber of the component tree. Call the beginWork method for each Fiber node traversed.

This method creates a sub-fiber node based on the incoming Fiber node and connects the two Fiber nodes.

The “homing” phase is entered when the leaf node (that is, the component with no child components) is traversed.

The “return” stage

CompleteWork is called during the homing phase to process the Fiber node.

When a Fiber node completes completeWork, if it has a sibling Fiber node (i.e. Fiber.sibling! == null), it enters the “pass” phase of its sibling Fiber.

If there is no sibling Fiber, the parent Fiber will go through the “home” phase.

The “recursion” and “return” phases are interlaced until the “return” to rootFiber. At this point, the work of the Render phase is over.

For example,

function App() {
	return (
		<div className="App">Learning the React<span>The source code</span>
		</div>
	);
}

export default App;

ReactDOM.render(<App />.document.getElementById('root'));

Copy the code

The corresponding Fiber tree structure

The Render phase executes in sequence:

  1. rootFiber beginWork
  2. App Fiber beginWork
  3. div Fiber beginWork
  4. Learning the React beginWork
  5. Learning the ReactcompleteWork
  6. span Fiber beginWork
  7. span Fiber completeWork
  8. div Fiber completeWork
  9. App Fiber completeWork
  10. rootFiber completeWork

The reason why there is no beginWork/completeWork of “source code” Fiber node is that as a means of performance optimization, React will deal with the Fiber with only a single text child node specially.

beginWork

You can see the beginWork source code here

BeginWork’s job is to pass in the current Fiber node and create sub-fiber nodes

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
 / /... Elliptic function body
}
Copy the code
  • Current: Fiber node at the last update of the Fiber node corresponding to the current component, that is, workinprogress.alternate;
  • WorkInProgress: Fiber node corresponding to the current component;
  • RenderLanes: priority related.

In the React dual cache mechanism, except for the rootFiber(the root node of the application), when the component is mounted, there is no Fiber node corresponding to the current component in the last update, because it is the first rendering. That is, mount current === null.

When the component update is updated, the current! = = null.

For this reason, beginWork’s work can be divided into two parts:

  • When mounting components: ExceptrootFiberCurrent === null. Different types of subfiber nodes are created depending on the fiber.tag;
  • Update: IfcurrentThe current node can be reused when certain conditions are met, so that the current. Child can be cloned as workinprogress. child without creating a new workinprogress. child.
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes
) :Fiber | null {

  // Update: if current has a possible optimization path, reuse current (i.e. last updated Fiber node)
  if(current ! = =null) {
    / /... Omit code

    / / reuse current
    return bailoutOnAlreadyFinishedWork(
      current,
      workInProgress,
      renderLanes,
    );
  } else {
    didReceiveUpdate = false;
  }

  // Mount: create different subfiber nodes depending on the tag
  switch (workInProgress.tag) {
    case IndeterminateComponent: 
      / /... omit
    case LazyComponent: 
      / /... omit
    case FunctionComponent: 
      / /... omit
    case ClassComponent: 
      / /... omit
    case HostRoot:
      / /... omit
    case HostComponent:
      / /... omit
    case HostText:
      / /... omit
    case SuspenseComponent:
     / /... omit
    / /... Omit other types}}Copy the code

When the update

DidReceiveUpdate === false (reuse the previous subfiber, no need to create new subfiber)

  1. oldProps === newProps && workInProgress.type === current.type, props and fiber.type remain the same;
  2. ! includesSomeLane(renderLanes, updateLanes), that is, the priority of the current Fiber node is insufficient;
if(current ! = =null) {
    // Omit the code...

    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if( oldProps ! == newProps || hasLegacyContextChanged() || (__DEV__ ? workInProgress.type ! == current.type :false)
    ) {
      didReceiveUpdate = true;
    } else if(! includesSomeLane(renderLanes, updateLanes)) { didReceiveUpdate =false;
     
      switch (workInProgress.tag) {
       // Omit the code...
      return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    } else {
      if((current.flags & ForceUpdateForLegacySuspense) ! == NoFlags) { didReceiveUpdate =true;
      } else {
      
        didReceiveUpdate = false; }}}else {
    didReceiveUpdate = false;
  }
Copy the code

When the mount

When the optimization path is not satisfied, we enter the second part and create a new subfiber. Depending on the fiber.tag, enter the logic for creating different types of fibers.

You can see the component type for the tag here

// Mount: create different subfiber nodes depending on the tag
  switch (workInProgress.tag) {
    case IndeterminateComponent: 
      / /... omit
    case LazyComponent: 
      / /... omit
    case FunctionComponent: 
      / /... omit
    case ClassComponent: 
      / /... omit
    case HostRoot:
      / /... omit
    case HostComponent:
      / /... omit
    case HostText:
      / /... omit
    case SuspenseComponent:
     / /... omit
    / /... Omit other types
  }
Copy the code

reconcileChildren

ReconcileChildren is the core part of the Reconciler module.

  1. For components at mount, it creates a new subfiber node;

  2. For an updated component, it compares the current component to the Fiber node corresponding to the component last updated (known as the Diff algorithm) and generates a new Fiber node from the result

export function reconcileChildren(
  current: Fiber | null,
  workInProgress: Fiber,
  nextChildren: any,
  renderLanes: Lanes,
) {
  if (current === null) {
 
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderLanes,
    );
  } else{ workInProgress.child = reconcileChildFibers( workInProgress, current.child, nextChildren, renderLanes, ); }}Copy the code

You can see the reconcileChildren source code here

Current === null? To distinguish the component mount phase from the Update phase. Finally, it will generate a new child Fiber node and assign the value to workinProgress. child as the return value of this beginWork.

The difference between reconcileChildFibers and mountChildFibers is that the passed Boolean values are different.

export const reconcileChildFibers = ChildReconciler(true);

export const mountChildFibers = ChildReconciler(false);
Copy the code

You can see the source code here

The ChildReconciler method primarily determines whether side effects are tracked.

function ChildReconciler(shouldTrackSideEffects) {

 function deleteChild(returnFiber: Fiber, childToDelete: Fiber) :void {
    if(! shouldTrackSideEffects) {// Noop.
      return;
    }
    const deletions = returnFiber.deletions;
    if (deletions === null) {
      returnFiber.deletions = [childToDelete];
      returnFiber.flags |= ChildDeletion;/ / tag effectTag
    } else{ deletions.push(childToDelete); }}... Omit code}Copy the code

You can see ChildReconciler in the source code here

effectTag

The work of the Render phase is done in memory, and the Renderer is notified of the DOM operations that need to be performed when the work is done. The specific types of DOM operations to be performed are stored in fiber.effectTag.

Here you can see the DOM operation that corresponds to the effectTag

// The DOM needs to be inserted into the page
export const Placement = / * * / 0b00000000000010;
// DOM needs to be updated
export const Update = / * * / 0b00000000000100;
// The DOM needs to be inserted into the page and updated
export const PlacementAndUpdate = / * * / 0b00000000000110;
// The DOM needs to be deleted
export const Deletion = / * * / 0b00000000001000; .Copy the code

When the Renderer phase inserts the DOM node corresponding to the Fiber node into the page, two conditions must be met:

  1. Fiber. StateNode exists, that is, the corresponding DOM node is stored in the fiber node.

  2. Placement effectTag exists in Fiber node;

StateNode will be created in completeWork. Only rootFiber(with current) will be assigned Placement effectTags on mount, and only one insert will be performed on commit.

The flow chart of beginWork

completeWork

You can see the source code for completeWork here

function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  const newProps = workInProgress.pendingProps;

  switch (workInProgress.tag) {
    case IndeterminateComponent:
    case LazyComponent:
    case SimpleMemoComponent:
    case FunctionComponent:
    case ForwardRef:
    case Fragment:
    case Mode:
    case Profiler:
    case ContextConsumer:
    case MemoComponent:
      return null;
    case ClassComponent: {
      / /... omit
      return null;
    }
    case HostRoot: {
      / /... omit
      updateHostContainer(workInProgress);
      return null;
    }
    case HostComponent: {
      / /... omit
      return null;
    }
  / /... omit
Copy the code

We focus on the HostComponent (the Fiber node for the native DOM component) that is required for page rendering.

HostComponent

case HostComponent: {
      popHostContext(workInProgress);
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      
      if(current ! = =null&& workInProgress.stateNode ! =null) {
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance,
        );

        // Update the case
       / /... omit
        
      } else {
         // Mount situation
        / /... omit
      }
      return null;
    }
Copy the code

Workinprogress. stateNode represents the DOM node corresponding to the Fiber node.

When the component update

When you update, the Fiber node already has a DOM node, so you don’t need to generate a DOM node. The main thing to do is to handle props

if(current ! = =null&& workInProgress.stateNode ! =null) {
        // Component update
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance,
        );

        if (current.ref !== workInProgress.ref) {
          markRef(workInProgress);
        }
    } 
Copy the code

You can see the updateHostComponent method definition here.

Inside the updateHostComponent, be processed props will be assigned to workInProgress. UpdateQueue, and eventually will be rendered on the page in the commit phase.

 updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
  
    var oldProps = current.memoizedProps;

    if (oldProps === newProps) {
      return;
    } 


    var instance = workInProgress.stateNode;
    var currentHostContext = getHostContext();

    var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); 

    workInProgress.updateQueue = updatePayload; 

    if(updatePayload) { markUpdate(workInProgress); }};Copy the code

UpdatePayload is in the form of an array. The value of its even-numbered index is the changed prop key, and the value of its odd index is the changed Prop value.

When the component is the mount

The main logic for mounting consists of three:

  1. Generate the corresponding DOM node for the Fiber node;
  2. Insert the descendant DOM node into the newly generated DOM node;
  3. Processes props similar to updateHostComponent in update logic;
const currentHostContext = getHostContext();

if(wasHydrated){
  ...
}else {
  // Create a DOM node for fiber
  const instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
        );
  // Insert the descendant DOM node into the newly generated DOM node
  appendAllChildren(instance, workInProgress, false.false);
  
  // The DOM node is assigned to fiber.statenode
  workInProgress.stateNode = instance;
  
  UpdateHostComponent = props; // updateHostComponent = props
   if( finalizeInitialChildren( instance, type, newProps, rootContainerInstance, currentHostContext, ) ) { markUpdate(workInProgress); }}Copy the code

The Placement effectTag will only exist in the rootFiber when mount. So how does the COMMIT phase insert the entire DOM tree into the page with a single DOM insert (corresponding to a Placement effectTag)?

The reason is the appendAllChildren method in completeWork.

Since completeWork is a function called in the “home” phase, each call to appendAllChildren inserts the generated descendant DOM node under the currently generated DOM node. So when we “revert” to rootFiber, we already have a constructed off-screen DOM tree.

effectList

As a basis for DOM operations, you need to find all Fiber nodes with effecttags in the commit phase and execute the corresponding effectTag operations in turn. If you go through the Fiber tree again during the commit phase and look for the effectTag! Fiber nodes with == NULL are obviously inefficient.

To solve this problem, in the upper function of completeWork, completeUnitOfWork, Each Fiber node that completes the completeWork and has an effectTag is stored in a unidirectional linked list called an effectList.

The first Fiber node in the effectList is stored in fiber.firsteffect, and the last element is stored in fiber.lasteffect.

Similar to appendAllChildren, during the “homing” phase, all Fiber nodes with effecttags are appented to the effectList, resulting in a unidirectional linked list starting with Rootfiber.firsteffect.

                         nextEffect         nextEffect
rootFiber.firstEffect -----------> fiber -----------> fiber
Copy the code

In the COMMIT phase, all effects are performed by simply traversing the effectList.

You can see the completeUnitOfWork code logic here.

Process the end

At this point, the render phase is complete. In the performSyncWorkOnRoot function, fiberRootNode is passed to the commitRoot method to start the commit process.

 const finishedWork: Fiber = (root.current.alternate: any);
  root.finishedWork = finishedWork;
  root.finishedLanes = lanes;
  commitRoot(root);
Copy the code

The flow chart of completeWork

The entire rendering process

export default class App extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			num: 0}; }updateNum() {
		const { num } = this.state;
		this.setState({ num: num + 1 });
	}

	render() {
		return (
			<div className="App">
				<div onClick={this.updateNum.bind(this)}>Learn the React - num: {this. State. Num}</div>
				<span>The source code</span>
			</div>
		);
	}
}

ReactDOM.render(<App />.document.getElementById('root'));

Copy the code

Take the class component above as an example

  1. rootFiber beginWork

  2. App Fiber beginWork

  3. Div Fiber beginWork with className “App”

  4. Div Fiber beginWork with onClick event

  5. “Learn React–num:” Fiber beginWork

  6. Learn React–num: Fiber completeWork

  7. Num is 0 Fiber beginWork

  8. Num is 0 Fiber completeWork

  9. Div Fiber completeWork for onClick event exists

  10. span Fiber beginWork

  11. span Fiber completeWork

  12. Div Fiber completeWork with className “App”

  13. App Fiber completeWork

  14. rootFiber completeWork

Component Mount phase

We know that the Render phase will first execute the beginWork method, which will only return a child fiber node. Take the above test Demo as an example to see the main process.

First, beginWork is the root node rootFiber of the current application. According to the principle of fiber dual caching, there are current Fiber and workInProgress of HostRoot with tag 3.

The second beginWork is App ClassComponent with tag 1. It has no current fiber but only workInProgress fiber.

The third element to enter beginWork is div HostComponent with tag of 5. It also has no current fiber but only workInProgress fiber.

The rest of the ellipsis…..

In order todivTake the example of HostComponent, and look at the main process of its mount phase.

beginWork

First, enter beginWork and enter updateHostComponent according to workinprogress. tag;
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {...switch (workInProgress.tag) {
     .....
     
    case HostComponent:
      returnupdateHostComponent(current, workInProgress, renderLanes); . }... }Copy the code
In the updateHostComponent method,var isDirectTextChild = shouldSetTextContent(type, nextProps);Is to determine if the current fiber has a unique text child node, if so, it will not create a child fiber node for it.It's an optimization.
function updateHostComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  pushHostContext(workInProgress);

  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }

  const type = workInProgress.type;
  const nextProps = workInProgress.pendingProps;
  constprevProps = current ! = =null ? current.memoizedProps : null;

  let nextChildren = nextProps.children;
  const isDirectTextChild = shouldSetTextContent(type, nextProps);

  if (isDirectTextChild) {
    nextChildren = null;
  } else if(prevProps ! = =null && shouldSetTextContent(type, prevProps)) {

    workInProgress.flags |= ContentReset;
  }

  markRef(current, workInProgress);
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}
Copy the code
UpdateHostComponent is calledreconcileChildrenMethod creates a subfiber node for the current workInProgress.

 reconcileChildren(current, workInProgress, nextChildren, renderLanes);
 
 return workInProgress.child;
  
Copy the code

In the reconcileChildren method, according tocurrent fiberWhether there is a different processing logic to enter. There is no current fiber in the mount phase, so entermountChildFibersMethods.
export function reconcileChildren(
  current: Fiber | null,
  workInProgress: Fiber,
  nextChildren: any,
  renderLanes: Lanes,
) {
  if (current === null) {
   
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderLanes,
    );
  } else{ workInProgress.child = reconcileChildFibers( workInProgress, current.child, nextChildren, renderLanes, ); }}Copy the code


export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);

Copy the code

MountChildFibers and reconcileChildFibers are both delivered to different Boolean values via the ChildReconciler method.

The ChildReconciler method returns the reconcileChildFibers method.

function ChildReconciler(shouldTrackSideEffects) {
 function deleteChild(returnFiber: Fiber, childToDelete: Fiber) :void {
    if(! shouldTrackSideEffects) {// Noop.
      return;
    }
    const deletions = returnFiber.deletions;
    if (deletions === null) {
      returnFiber.deletions = [childToDelete];
      returnFiber.flags |= ChildDeletion;
    } else{ deletions.push(childToDelete); }}...return reconcileChildFibers;
}

Copy the code

ShouldTrackSideEffects says whether to track side effects, returns directly if not, and marks it if it does.

reconcileChildFibers

In reconcileChildFibers method, different processing logic is entered by judging different types of newChild. If newChild is an array, it enters the processing logic of reconcileChildrenArray.

function reconcileChildFibers(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    newChild: any,
    lanes: Lanes,
  ) :Fiber | null {
   
    const isUnkeyedTopLevelFragment =
      typeof newChild === 'object'&& newChild ! = =null &&
      newChild.type === REACT_FRAGMENT_TYPE &&
      newChild.key === null;
    if (isUnkeyedTopLevelFragment) {
      newChild = newChild.props.children;
    }

 
    if (typeof newChild === 'object'&& newChild ! = =null) {...if (isArray(newChild)) {
        returnreconcileChildrenArray( returnFiber, currentFirstChild, newChild, lanes, ); }...return deleteRemainingChildren(returnFiber, currentFirstChild);
  }
Copy the code

reconcileChildrenArray

The newFiber node is created in the reconcileChildrenArray method, but only one is created.

We know from the figure above that the createChild method is called.

But only one fiber node will be created

createChild

The createChild method enters different processing logic depending on the type of newChild.

createFiberFromElement

CreateFiberFromElement method will be called createFiberFromTypeAndProps returns and fiber.

export function createFiberFromElement(element: ReactElement, mode: TypeOfMode, lanes: Lanes,) :Fiber {
  let owner = null;
  
  const type = element.type;
  const key = element.key;
  const pendingProps = element.props;
  const fiber = createFiberFromTypeAndProps(
    type,
    key,
    pendingProps,
    owner,
    mode,
    lanes,
  );
 
  return fiber;
}
Copy the code
createFiberFromTypeAndProps

The createFiber method is then called to create the fiber node.

createFiber
var createFiber = function (tag, pendingProps, key, mode) {

  return new FiberNode(tag, pendingProps, key, mode);
  
};

Copy the code

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

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.flags = NoFlags;
  this.subtreeFlags = NoFlags;
  this.deletions = null;

  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  this.alternate = null;
}
Copy the code
The call stack for the “pass” phase

completeWork

< span style = “font-size: 16px;” The second entry into the completeWork, also with tag 6, is the “0” text fiber node; The third entry to completeWork is a HostComponent with tag 5, elementType “div” and an onClick event. Let’s use this fiber node as an example.


function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {

 const newProps = workInProgress.pendingProps;
 switch (workInProgress.tag) {
   ....
   
   case HostComponent: {
      popHostContext(workInProgress);
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      if(current ! = =null&& workInProgress.stateNode ! =null) {
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance,
        );

        if (current.ref !== workInProgress.ref) {
          markRef(workInProgress);
        }
      } else {
        if(! newProps) { bubbleProperties(workInProgress);return null;
        }

        const currentHostContext = getHostContext();
      
        if(wasHydrated) { ..... }}else {
          const instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );

          appendAllChildren(instance, workInProgress, false.false);

          workInProgress.stateNode = instance;

          if( finalizeInitialChildren( instance, type, newProps, rootContainerInstance, currentHostContext, ) ) { markUpdate(workInProgress); }}if(workInProgress.ref ! = =null) {
          markRef(workInProgress);
        }
      }
      bubbleProperties(workInProgress);
      return null; }... }}Copy the code
createInstance

The createInstance method creates the corresponding DOM node.

  const instance = createInstance(type,newProps,rootContainerInstance,
           currentHostContext,
            workInProgress);
Copy the code

createElement

The createElement method is called in the createInstance method to create the DOM node.

function createElement(type, props, rootContainerElement, parentNamespace) {
  var isCustomComponentTag; 

  var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement);
  var domElement;
  var namespaceURI = parentNamespace;

  if (namespaceURI === HTML_NAMESPACE) {
    namespaceURI = getIntrinsicNamespace(type);
  }

  if (namespaceURI === HTML_NAMESPACE) {
    {
      isCustomComponentTag = isCustomComponent(type, props); // Should this check be gated by parent namespace? Not sure we want to
      // allow <SVG> or <mATH>.

      if(! isCustomComponentTag && type ! == type.toLowerCase()) { error('<%s /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', type); }}if (type === 'script') {
      // Create the script via .innerHTML so its "parser-inserted" flag is
      // set to true and it does not execute
      var div = ownerDocument.createElement('div');

      div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
      // This is guaranteed to yield a script element.

      var firstChild = div.firstChild;
      domElement = div.removeChild(firstChild);
    } else if (typeof props.is === 'string') {
      // $FlowIssue `createElement` should be updated for Web Components
      domElement = ownerDocument.createElement(type, {
        is: props.is
      });
    } else {
    
      domElement = ownerDocument.createElement(type); 

      if (type === 'select') {
        var node = domElement;

        if (props.multiple) {
          node.multiple = true;
        } else if(props.size) { node.size = props.size; }}}}else {
    domElement = ownerDocument.createElementNS(namespaceURI, type);
  }

  {
    if (namespaceURI === HTML_NAMESPACE) {
      if(! isCustomComponentTag &&Object.prototype.toString.call(domElement) === '[object HTMLUnknownElement]' && !hasOwnProperty.call(warnedUnknownTags, type)) {
        warnedUnknownTags[type] = true;

        error('The tag <%s> is unrecognized in this browser. ' + 'If you meant to render a React component, start its name with ' + 'an uppercase letter.', type); }}}return domElement;
}
Copy the code
appendAllChildren

The appendAllChildren method inserts the created DOM into the previously created DOM tree.

 var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
  
 appendAllChildren(instance, workInProgress, false.false);
 
 workInProgress.stateNode = instance; 
           

 if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance)) {
              markUpdate(workInProgress);
            }
Copy the code
workInProgress.stateNode = instance;

Assign the created DOM node to the stateNode of the workInProgress fiber.

finalizeInitialChildren

The finalizeInitialChildren method sets the properties for the DOM node.

performSyncWorkOnRoot

When all nodes complete the completeWork, the other logic of performSyncWorkOnRoot is entered.

function performSyncWorkOnRoot(root) {...var finishedWork = root.current.alternate;
  
  root.finishedWork = finishedWork;
  
  root.finishedLanes = lanes;
  
  commitRoot(root); // Enter the COMMIT phase

  ensureRootIsScheduled(root, now());
  return null;
}
Copy the code

Root.current. alternate points to the workInProgress Fiber node created by the current update.

Component Update phase

When we click on the div tag to change the value of Num, the component enters the update phase.

React–num:{this.state.num}

div

beginWork

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {...if(current ! = =null) {
      var oldProps = current.memoizedProps;
      var newProps = workInProgress.pendingProps;
       if(oldProps ! == newProps || hasContextChanged() || ( workInProgress.type ! == current.type )) { didReceiveUpdate =true; }... }else {
    didReceiveUpdate = false;
  }
  workInProgress.lanes = NoLanes;
  switch (workInProgress.tag) {
    ...
    case HostComponent:
      returnupdateHostComponent(current, workInProgress, renderLanes); . }... }Copy the code

Current Fiber exists at this point in the component update phase, and the didReceiveUpdate = true condition is set, followed by the updateHostComponent logic.

updateHostComponent
function updateHostComponent(current, workInProgress, renderLanes) {... reconcileChildren(current, workInProgress, nextChildren, renderLanes);return workInProgress.child;
  
}
Copy the code

The updateHostComponent method reconcileChildren is invoked;

reconcileChildren

The reconcileChildren method compares current fiber and nextChildren, and creates a new fiber node and returns the result of the comparison.

completeWork

function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  const newProps = workInProgress.pendingProps;
  switch (workInProgress.tag) {
    ...
    case HostComponent: {
      ....
      if(current ! = =null&& workInProgress.stateNode ! =null) {
        updateHostComponent$1( current, workInProgress, type, newProps, rootContainerInstance, ); . }... }... }Copy the code
updateHostComponent$1
   updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {...var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); 

    workInProgress.updateQueue = updatePayload; 

    if(updatePayload) { markUpdate(workInProgress); }};Copy the code

The most important method in the updateHostComponent$1 method is the prepareUpdate method, which is concerned with property changes.

prepareUpdate
function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {{...return diffProperties(domElement, type, oldProps, newProps);
}
Copy the code
diffProperties
function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {...return updatePayload;
}
Copy the code

Return to updatePayload diffProperties method finally, and then assigned to workInProgress. UpdateQueue.

From here, the main update process in the Render phase is over.

The resources

React Technology revealed