This is the 9th day of my participation in the August More Text Challenge. For details, see:August is more challenging

In the previous introduction to Fiber Tree Construction (first creation), we demonstrated the gradual construction of the fiber tree in the fiber tree construction cycle. Since this is the first creation, all nodes are created during construction, and no old nodes are reused.

This section discusses the case of contrast updates (analyzed in Legacy mode). Before reading this section, it is a good idea to have some understanding of the Fiber tree construction (first creation), which has a lot of similar logic that will not be repeated. This section focuses on the difference between contrast updates and first creation.

The example code for this section is as follows (codesandbox address):

import React from 'react';

class App extends React.Component {
  state = {
    list: ['A'.'B'.'C']}; onChange =() = > {
    this.setState({ list: ['C'.'A'.'X']}); };componentDidMount() {
    console.log(`App Mount`);
  }
  render() {
    return (
      <>
        <Header />
        <button onClick={this.onChange}>change</button>
        <div className="content">
          {this.state.list.map(item => (
            <p key={item}>{item}</p>
          ))}
        </div>
      </>); }}class Header extends React.PureComponent {
  render() {
    return (
      <>
        <h1>title</h1>
        <h2>title2</h2>
      </>); }}export default App;
Copy the code

inFirst apply colours to a drawingAfter completion, withFiber treeThe related memory structure is shown belowCompared to updateProcess) :

Update the entrance

Among the four stages (from input to output) summarized in the reconciler process, the only function that accepts the input is scheduleUpdateOnFiber(source address). In the React-Reconciler’s exposed API, scheduleUpdateOnFiber is indirectly called whenever an operation needs to change the fiber (either a first render or a comparison update). The scheduleUpdateOnFiber function is a mandatory route on the input link.

There are three update methods

There are three common ways to initiate an update:

  1. ClassComponent callsetState.
  2. FunctionComponent callhookObject is exposeddispatchAction.
  3. incontainerRepeat calls on the noderender(Website sample)

The source code for these three updates is listed below:

setState

Mount setState(source link) on the Prototype of the Component object:

Component.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Copy the code

During the beginWork phase of the fiber tree construction (initial creation), the this.updater object is the following after the class component is initialized:

const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
    // 1. Obtain the fiber node corresponding to the class instance
    const fiber = getInstance(inst);
    // 2. Create an update object
    const eventTime = requestEventTime();
    const lane = requestUpdateLane(fiber); // Determine the priority of the current update object
    const update = createUpdate(eventTime, lane);
    update.payload = payload;
    if(callback ! = =undefined&& callback ! = =null) {
      update.callback = callback;
    }
    Add the update object to the updateQueue queue of the current Fiber node
    enqueueUpdate(fiber, update);
    // 4. Enter the 'input' section of the reconcier operation process
    scheduleUpdateOnFiber(fiber, lane, eventTime); // The lane passed in is the update priority}};Copy the code

dispatchAction

This is just to compare dispatchAction and setState. The in-depth analysis of hook principle is discussed in detail in the section of Hook principle.

In a function type component, if you use hook(useState), you can update it through the dispatchAction(source link) exposed by the Hook API

function dispatchAction<S.A> (fiber: Fiber, queue: UpdateQueue
       
        , action: A,
       ,>) {
  // 1. Create an update object
  const eventTime = requestEventTime();
  const lane = requestUpdateLane(fiber); // Determine the priority of the current update object
  const update: Update<S, A> = {
    lane,
    action,
    eagerReducer: null.eagerState: null.next: (null: any),
  };
  // 2. Add the update object to the updateQueue queue of the current Hook object
  const pending = queue.pending;
  if (pending === null) {
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  queue.pending = update;
  // 3. Request scheduling to enter the 'input' section of the reconcier operation process.
  scheduleUpdateOnFiber(fiber, lane, eventTime); // The lane passed in is the update priority
}
Copy the code

Call render repeatedly

import ReactDOM from 'react-dom';
function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
Copy the code

For repeated render, it is explained during the React application startup, and the call path includes updateContainer–>scheduleUpdateOnFiber

Therefore, no matter where you update from, you will eventually enter scheduleUpdateOnFiber, which again proves that scheduleUpdateOnFiber is a necessary function in the input phase (refer to the Reconciler process).

Construction phase

ScheduleUpdateOnFiber:

/ /... Omit part of the code
export function scheduleUpdateOnFiber(
  fiber: Fiber, // Fiber indicates the node to be updated
  lane: Lane, // Lane indicates the update priority
  eventTime: number,
) {
  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
  if (lane === SyncLane) {
    if( (executionContext & LegacyUnbatchedContext) ! == NoContext && (executionContext & (RenderContext | CommitContext)) === NoContext ) {// First render
      performSyncWorkOnRoot(root);
    } else {
      // Compare update
      ensureRootIsScheduled(root, eventTime);
    }
  }
  mostRecentlyUpdatedRoot = root;
}
Copy the code

Compare the differences between the update and the first render:

  1. markUpdateLaneFromFiberToRootFunction, only inCompared to updateThe stage does its job. It finds outFiber treeBy this timeupdateAffect all nodes and set those nodesfiber.lanesorfiber.childLanes(in thelegacyMode forSyncLane) forFiber tree structurePhase use.
function markUpdateLaneFromFiberToRoot(
  sourceFiber: Fiber, // sourceFiber indicates the node to be updated
  lane: Lane, // Lane indicates the update priority
) :FiberRoot | null {
  // 1. Set update priority to sourcefiber.lanes
  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
  let alternate = sourceFiber.alternate;
  if(alternate ! = =null) {
    // Set the sourceFiber. Alternate priority at the same time
    alternate.lanes = mergeLanes(alternate.lanes, lane);
  }
  // 2. Start from sourceFiber and go up to HostRoot. Set childLanes on all nodes (including alternate) along the route
  let node = sourceFiber;
  let parent = sourceFiber.return;
  while(parent ! = =null) {
    parent.childLanes = mergeLanes(parent.childLanes, lane);
    alternate = parent.alternate;
    if(alternate ! = =null) {
      alternate.childLanes = mergeLanes(alternate.childLanes, lane);
    }
    node = parent;
    parent = parent.return;
  }
  if (node.tag === HostRoot) {
    const root: FiberRoot = node.stateNode;
    return root;
  } else {
    return null; }}Copy the code

markUpdateLaneFromFiberToRoot

Below expressed markUpdateLaneFromFiberToRoot specific role:

  • In order tosourceFiberFor the starting point, set the starting pointfiber.lanes
  • You start at the beginning, you go to the beginningHostRootFiber, set all nodes on the parent path (includingfiber.alternate)fiber.childLanes.
  • By setting thefiber.lanesandfiber.childLanesYou can help determine whether the subtree needs to be updated (below)Loop structure).

  1. Compared to updateNo direct callperformSyncWorkOnRootInstead, it is handled through the dispatch center, as this example is inLegacyMode, and it will be executed synchronouslyperformSyncWorkOnRootThe detailed principle can refer toReact Scheduler Scheduler). So it calls the linkperformSyncWorkOnRoot--->renderRootSync--->workLoopSyncwithPrimary structuralIs consistent with.

In renderRootSync:

function renderRootSync(root: FiberRoot, lanes: Lanes) {
  const prevExecutionContext = executionContext;
  executionContext |= RenderContext;
  // If fiberRoot changes, or update.lane changes, the stack frame is refreshed and the last render progress is discarded
  if(workInProgressRoot ! == root || workInProgressRootRenderLanes ! == lanes) {// Refresh the stack frame, both in Legacy mode
    prepareFreshStack(root, lanes);
  }
  do {
    try {
      workLoopSync();
      break;
    } catch(thrownValue) { handleError(root, thrownValue); }}while (true);
  executionContext = prevExecutionContext;
  // Reset the global variable to indicate the end of render
  workInProgressRoot = null;
  workInProgressRootRenderLanes = NoLanes;
  return workInProgressRootExitStatus;
}
Copy the code

Before entering the loop construct (workLoopSync), the stack frame is refreshed (prepareFreshStack is called)(see stack frame management in Fiber Tree Construct (Basic preparation)).

The memory structure is as follows:

Note:

  • fiberRoot.currentPoints to the corresponding pageFiber tree.workInProgressPointing to what is being constructedFiber tree.
  • The refresh stack frame is calledcreateWorkInProgress(),WorkInProgress. Flags and workInProgress. EffectsHave been reset. AndworkInProgress.child = current.child.So in the entryLoop structureBefore,HostRootFiberwithHostRootFiber.alternateSharing achild(here,fiber(<App/>)).

Loop structure

Review the introduction in fiber tree construction (first creation). The entire Fiber tree construction is a depth-first traversal (see React depth-first traversal). There are two important variables workInProgress and current(see double buffering techniques introduced in The Fiber Tree construction (foundation preparation)):

  • workInProgressandcurrentAre all Pointers
  • workInProgressPoints to what is currently under constructionfibernode
  • current = workInProgress.alternate(i.e.fiber.alternate) to which the page is currently in usefiberNode.

In depth-first traversal, each fiber node goes through two stages:

  1. Exploring stagebeginWork
  2. Back stagecompleteWork

Together, these two stages create (or update) each fiber node, all of which make up the Fiber tree.

function workLoopSync() {
  while(workInProgress ! = =null) { performUnitOfWork(workInProgress); }}/ /... Omit some irrelevant code
function performUnitOfWork(unitOfWork: Fiber) :void {
  // unitOfWork is the workInProgress passed in
  const current = unitOfWork.alternate;
  let next;
  next = beginWork(current, unitOfWork, subtreeRenderLanes);
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    // If no new node is derived, the completeWork phase is entered, passing in the current unitOfWork
    completeUnitOfWork(unitOfWork);
  } else{ workInProgress = next; }}Copy the code

Note: current = unitofwork. alternate; If it is not null, the current passed here will be heavily used in the subsequent call logic.

The search stage beginWork

BeginWork (Current, unitOfWork, subtreeRenderLanes)(source address).

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  const updateLanes = workInProgress.lanes;
  if(current ! = =null) {
    // Enter the comparison
    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)) {// The current render priority does not include fiber. Lanes, indicating that the current fiber node does not need to be updated
      didReceiveUpdate = false;
      switch (
        workInProgress.tag
        // The switch statement contains context logic, which will not be discussed in this section.) {}/ / the current fiber nodes do not need to update, call bailoutOnAlreadyFinishedWork loop testing whether child nodes need to be updated
      returnbailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); }}// The remaining logic is shared with the initial creation
  // 1. Set workInProgress priority to NoLanes(highest priority)
  workInProgress.lanes = NoLanes;
  // 2. Based on the type of the workInProgress node, use different methods to derive child nodes
  switch (
    workInProgress.tag // List only some cases
  ) {
    case ClassComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderLanes,
      );
    }
    case HostRoot:
      return updateHostRoot(current, workInProgress, renderLanes);
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderLanes);
    case HostText:
      return updateHostText(current, workInProgress);
    case Fragment:
      returnupdateFragment(current, workInProgress, renderLanes); }}Copy the code

bailoutLogic {# bailout}

In the source code, bailout is used to judge whether subtree node is fully multiplexed, if it can be multiplexed, fiber tree structure will be skipped.

Unlike the initial creation, during the comparison update process, if the node is an old node, the current! == null, need to compare, and then decide whether to reuse the old node and its subtree (i.e., bailout logic).

  1. ! includesSomeLane(renderLanes, updateLanes)The judgment branch containsRender priorityandUpdate the priority(Details can be reviewedFiber Tree structure (foundation preparation)In thepriorityIf the current node does not need to be updated, thebailoutLogic.
  2. And then at the end of the callbailoutOnAlreadyFinishedWork:
    • If both of them satisfy! includesSomeLane(renderLanes, workInProgress.childLanes), indicating that the fiber node and its subtree do not need to be updated and can directly enter the backtracking phase (completeUnitofWork)
    • If not,! includesSomeLane(renderLanes, workInProgress.childLanes), which means that the child node needs to be updated,cloneAnd returns the child node.
// Omit some irrelevant code
function bailoutOnAlreadyFinishedWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  if(! includesSomeLane(renderLanes, workInProgress.childLanes)) {/ / render priority. Do not include workInProgress childLanes, suggests that children also need not update. Return NULL and enter the traceback phase directly.
    return null;
  } else {
    Clone the fiber and return the child node
    cloneChildFibers(current, workInProgress);
    returnworkInProgress.child; }}Copy the code

Note: cloneChildFibers calls createWorkInProgress internally, and workinProgress. alternate(without creating new memory space) is preferentially multiplexed when constructing the fiber node, otherwise a new fiber object will be created.

updateXXXfunction

The updateXXX function (for example: The main logic of updateHostRoot, updateClassComponent, etc.) is exactly the same as the initial construction process, and the overall purpose is to generate children down and invoke reconcileChildren harmonization functions in the process. As long as the fiber node has side effects, Special operations are set to fiber-. flags(e.g. node ref,class lifecycle,function hook, node delete, etc.).

Compare the differences in the update process:

  1. bailoutOnAlreadyFinishedWork
    • Compared to updateIf the current node is encountered, no update is required (for example:classType of a node andshouldComponentUpdatereturnfalse) will be re-enteredbailoutLogic.
  2. reconcileChildrenHarmonic functions
    • The harmonic function isupdateXXXFunction, which is used to generate child nodes down and setfiber.flags.
    • For the first time to createwhenfiberNodes don’t have comparison objects, so there’s no extra logic when you go down to generate children, just create them.
    • Compared to updateWhen need toReactElementObject and theThe old fiberObjects to determine whether reuse is requiredThe old fiberObject.

Note: The focus of this section is the construction of fiber tree. In the process of comparative updating, the Diff algorithm implemented by reconcileChildren() function is very important, but it is only at the algorithm level. The implementation of diff algorithm is analyzed separately in the harmonic algorithm of React algorithm.

In this section, we only need to understand the purpose of the harmonic function:

  1. Settings for adding, moving, and deleting nodesfiber.falgs(Add, move:PlacementDelete:Deletion)
  2. If it needs to be deletedfiber.Except for the selfDeletionIn addition, it is added to the parent node’seffectsIn the listNormal side effect queue processing is incompleteWorkFunction, but the node is removedfiberTree, no more entrycompleteWorkStage, so inbeginWorkPhase is early added to the side effects queue).

Backtrack phase completeWork

The completeUnitOfWork(unitOfWork) function (source address) is created for the first time and compared with the update logic, both process the fiber node that has been created in the beginWork phase, and finally create (update) THE DOM object, and move up the side effect queue.

We’re going to focus here on the completeWork function, current! == null case:

/ /... Omit irrelevant code
function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  const newProps = workInProgress.pendingProps;
  switch (workInProgress.tag) {
    case HostComponent: {
      // Non-text nodes
      popHostContext(workInProgress);
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      if(current ! = =null&& workInProgress.stateNode ! =null) {
        // Handle the changes
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance,
        );
        if (current.ref !== workInProgress.ref) {
          markRef(workInProgress);
        }
      } else {
        / /... Omit irrelevant code
      }
      return null;
    }
    case HostText: {
      // Text node
      const newText = newProps;
      if(current && workInProgress.stateNode ! =null) {
        const oldText = current.memoizedProps;
        // Handle the changes
        updateHostText(current, workInProgress, oldText, newText);
      } else {
        / /... Omit irrelevant code
      }
      return null; }}}Copy the code
updateHostComponent = function(current: Fiber, workInProgress: Fiber, type: Type, newProps: Props, rootContainerInstance: Container,) {
  const oldProps = current.memoizedProps;
  if (oldProps === newProps) {
    return;
  }
  const instance: Instance = workInProgress.stateNode;
  const currentHostContext = getHostContext();
  const updatePayload = prepareUpdate(
    instance,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
    currentHostContext,
  );
  workInProgress.updateQueue = (updatePayload: any);
  / / if you have attribute changes, set the fiber. The flags | = Update, waiting for ` commit ` stage of processing
  if(updatePayload) { markUpdate(workInProgress); }}; updateHostText =function(current: Fiber, workInProgress: Fiber, oldText: string, newText: string,) {
  / / if you have attribute changes, set the fiber. The flags | = Update, waiting for ` commit ` stage of processing
  if (oldText !== newText) {
    markUpdate(workInProgress);
  }
};
Copy the code

DOM attributes can be seen in the process of updating, if have change, build the DOM object will not again, but set fiber. The flags | = Update, waiting for the commit phase processing (source link).

Process diagram

For the sample code in this section, represent the entire fiber tree construction process:

Before construction:

PrepareFreshStack is called to refresh the stack frame before entering the loop, and this initialization state is maintained until entering the fiber tree loop:

PerformUnitOfWork First call (beginWork only):

  • Perform before:workInProgressPoint to theHostRootFiber.alternateObject, at which pointcurrent = workInProgress.alternatePoints to the corresponding pagefiberThe tree.
  • Implementation process:
    • becausecurrent ! == nullAnd the current nodefiber.lanesNot inRender priorityRange, so enterbailoutOnAlreadyFinishedWorklogic
    • And becausefiber.childLanesIn aRender priorityScope, proofchildNodes need to be updated, clonedworkInProgress.childNode.
    • cloneAfter that,The new fiberNodes are droppedThe old fiberThe flag bit on (flags) and side effects (effects), the other properties are retained.
  • After execution: Returned bycloneSub node offiber(<App/>), the mobileworkInProgressPointing to child nodesfiber(<App/>)

PerformUnitOfWork second call (beginWork only):

  • Perform before:workInProgressPoint to thefiber(<App/>)Node, andcurrent = workInProgress.alternateHave a value
  • Implementation process:
    • The current nodefiber.lanesIn aRender priorityWithin range, it will enterupdateClassComponent()function
    • inupdateClassComponent()Function, callreconcilerChildren()Generate child nodes.
  • After execution: Returns the lower-level nodefiber(<Header/>), the mobileworkInProgressPointing to child nodesfiber(<Header/>)

PerformUnitOfWork the third call (perform beginWork and completeUnitOfWork):

  • beginWorkPerform before:workInProgressPoint to thefiber(<Header/>)And,current = workInProgress.alternateHave a value
  • beginWorkImplementation process:
    • The current nodefiber.lanesIn aRender priorityWithin range, it will enterupdateClassComponent()function
    • inupdateClassComponent()Function, because this component isPureComponent.shouldComponentUpdateJudged to befalse, enter thebailoutOnAlreadyFinishedWorkLogic.
    • And becausefiber.childLanesNot inRender priorityScope, proofchildNodes do not need to be updated
  • beginWorkAfter execution: because fully satisfiedbailoutLogic, returnnull.So entercompleteUnitOfWork(unitOfWork)Function, the parameters passed inunitOfWorkIt’s essentiallyworkInProgress(This point points tofiber(<Header/>))

  • completeUnitOfWorkPerform before:workInProgressPoint to thefiber(<Header/>)
  • completeUnitOfWorkImplementation process: tofiber(<Header/>)For the starting point, back up

CompleteUnitOfWork loop 1:

  1. performcompleteWorkFunction:classType of components do not need to be handled.
  2. Move up side queue: due to this nodefiber(header)No side effects (fiber.flags = 0), so the side effect queue does not change substantially after execution (it is currently empty).
  3. Backtracking upwards: since there are sibling nodes, theworkInProgressPoint to the next sibling nodefiber(button)To quitcompleteUnitOfWork.

PerformUnitOfWork fourth call (perform beginWork and completeUnitOfWork):

  • BeginWork Execution process: Calls updateHostComponent

    • In this examplebuttonThe child node of is aDirect text nodeTo set upnextChildren = null(The source code comment explains that there is no need to open up memory to create a text node, while reducing traversal).
    • Due to thenextChildren = null, afterreconcilerChildrenAfter phase processing, the return value is alsonull
  • BeginWork: Since the lower-level node is null, enter the completeUnitOfWork(unitOfWork) function, and the passed parameter unitOfWork is actually workInProgress(at this time, pointing to the fiber(button) node)

  • CompleteUnitOfWork execution process: Take fiber(button) as the starting point and backtrack upward

CompleteUnitOfWork loop 1:

  1. performcompleteWorkfunction
    • becausefiber(button).stateNode ! = null, so there is no need to create the DOM object again. You just need to make further callsupdateHostComponent()Records DOM property changes
    • inupdateHostComponent()In the function, becauseoldProps === newProps, so there is no need to record the changes and return directly
  2. Move up side queue: due to this nodefiber(button)No side effects (fiber.flags = 0), so the side effect queue does not change substantially after execution (it is currently empty).
  3. Backtracking upwards: since there are sibling nodes, theworkInProgressPoint to the next sibling nodefiber(div)To quitcompleteUnitOfWork.

PerformUnitOfWork the fifth call (beginWork):

  • Perform before:workInProgressPoint to thefiber(div)Node, andcurrent = workInProgress.alternateHave a value
  • Implementation process:
    • inupdateHostComponent()Function, callreconcilerChildren()Generate child nodes.
    • Note that the child nodes are an iterable array that will putfiber.child.sblingBuild them together and set them up as neededfiber.flagsIn this example, the child node is deleted, and the deleted node is added to the side effect queue of the parent node. For details, seeReact algorithm harmonic algorithm).
  • After execution: Returns the lower-level nodefiber(p), the mobileworkInProgressPointing to child nodesfiber(p)

PerformUnitOfWork sixth call (perform beginWork and completeUnitOfWork):

  • BeginWork execution process: Completely consistent with the logic of constructing fiber(Button) in the 4th call, since both are direct text nodes, and the subordinate child nodes returned by reconcilerChildren() are null.

  • After beginWork: Since the lower-level node is null, the completeUnitOfWork(unitOfWork) function is entered

  • CompleteUnitOfWork execution process: Take fiber(p) as the starting point and backtrack upward

CompleteUnitOfWork loop 1:

  1. performcompleteWorkfunction
    • becausefiber(p).stateNode ! = null, so there is no need to create the DOM object again. inupdateHostComponent()Function, and because the node attributes do not change, there is no need to flag
  2. Move up side queue: this nodefiber(p)No side effects (fiber.flags = 0).
  3. Backtracking upwards: since there are sibling nodes, theworkInProgressPoint to the next sibling nodefiber(p)To quitcompleteUnitOfWork.

PerformUnitOfWork the seventh call (perform beginWork and completeUnitOfWork):

  • BeginWork execution process: Completely consistent with the logic of constructing fiber(Button) in the 4th call, since both are direct text nodes, and the subordinate child nodes returned by reconcilerChildren() are null.

  • After beginWork: Since the lower-level node is null, the completeUnitOfWork(unitOfWork) function is entered

  • CompleteUnitOfWork execution process: Take fiber(p) as the starting point and backtrack upward

CompleteUnitOfWork loop 1:

  1. Execute the completeWork function:

    • becausefiber(p).stateNode ! = null, so there is no need to create the DOM object again. inupdateHostComponent()Function, and because the node attributes do not change, there is no need to flag
  2. Move up side effect queue: This node fiber(p) has side effects (fiber.flags = Placement) and needs to be added after the parent node’s side effect queue.

  3. Backtracking up: Since there are sibling nodes, point workInProgress to the next sibling node fiber(p) and exit completeUnitOfWork.

PerformUnitOfWork eighth call (perform beginWork and completeUnitOfWork):

  • BeginWork: This node fiber(p) is a new node, its current === null, will enter the updateHostComponent() function. Because it is a direct text node, reconcilerChildren() returns a subordinate child node of null.

  • After beginWork: Since the lower-level node is null, the completeUnitOfWork(unitOfWork) function is entered

  • CompleteUnitOfWork execution process: Take fiber(p) as the starting point and backtrack upward

CompleteUnitOfWork loop 1:

  1. performcompleteWorkFunction: because this node is a new node, andfiber(p).stateNode === null, so createfiber(p)Node correspondingDOMInstance, mount tofiber.stateNodeAbove the law.
  2. Move up side queue: this nodefiber(p)Side effects (fiber.flags = Placement), which needs to be added after the parent node’s side effect queue.
  3. Backtracking upwards: since there is no sibling node, theworkInProgressThe pointer points to the parentfiber(div).

CompleteUnitOfWork loop 2:

  1. performcompleteWorkFunction: due todivComponent has no property changes, soupdateHostComponent()No side effect flag is set
  2. Move up side queue: this nodefiber(div)Is added to the parent node’s side effect queue.
  3. Backtracking upwards: since there is no sibling node, theworkInProgressThe pointer points to the parentfiber(<App/>)

CompleteUnitOfWork loop 3:

  1. performcompleteWorkFunction: Nodes of type class need not be processed
  2. Move up side queue: this nodefiber(<App/>)Is added to the parent node’s side effect queue.
  3. Backtracking upwards: since there is no sibling node, theworkInProgressThe pointer points to the parentfiber(HostRootFiber)

CompleteUnitOfWork loop 4:

  1. performcompleteWorkFunction:HostRootThis operation is not required for the node type
  2. Backtracking up: Since the parent node is empty, there is no need to enter the logic to process the side effect queue. The last setworkInProgress=nullAnd out ofcompleteUnitOfWork
  3. resetfiber.childLanes

At this point, the entire fiber tree construction loop (comparison update) has been completed, a new fiber tree has been created, and the side effect queue has been mounted on the root node of the fiber tree. 3, no render is in progress. And mount the latest fiber tree onto fiberRoot. FinishedWork. At this point, the memory structure of the entire fiber tree is as follows (note the FiberRoot.finishedWork and Fiberroot.current Pointers, which are processed during the commitRoot phase):

After the fiber tree is constructed, the remaining logic is almost the same, regardless of the initial construction or the comparative update, and will be discussed in the Fiber Tree rendering section.

conclusion

This section demonstrates the entire process of constructing the Fiber tree during the update phase (contrast update), tracking how memory references change during the creation. The biggest difference from the first construction lies in whether the fiber node can be multiplexed, in which bailout logic is the judgment basis of whether the fiber subtree can be multiplexed.

Write in the last

This article belongs to the diagram react source code series in the operation of the core plate, this series of nearly 20 articles, really in order to understand the React source code, and then improve the architecture and coding ability.

The first draft of the graphic section has been completed and will be updated in August. If there are any errors in the article, we will correct them as soon as possible on Github.