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

This section is based on ReACTV17.0.2 and builds entirely on the basics introduced earlier in Fiber Tree construction (Foundation preparation), which summarizes two cases of fiber tree construction:

  1. First created: inReactWhen the app is first launched, the interface has not yet been rendered, and the comparison process does not begin. It is equivalent to constructing a completely new tree.
  2. Comparison update:ReactAfter the application starts, the interface has been rendered. If the update occurs again, createThe new fiberBefore you need to sumThe old fiberThe resulting fiber tree may be completely new or partially updated.

In this section, we only discuss the initial creation of the fiber tree. In order to control the length of the process (this section will go straight to the core source code and not cover the basics, see Fiber Tree construction (foundation preparation)) and highlight the fiber tree construction process, we will analyze it later in Legacy mode (since we only discuss the principle of fiber tree construction). Concurrent mode is no different from Legacy).

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

class App extends React.Component {
  componentDidMount() {
    console.log(`App Mount`);
    console.log('App component corresponding fiber node:'.this._reactInternals);
  }
  render() {
    return (
      <div className="app">
        <header>header</header>
        <Content />
      </div>); }}class Content extends React.Component {
  componentDidMount() {
    console.log(`Content Mount`);
    console.log(The fiber node corresponding to the 'Content component:'.this._reactInternals);
  }
  render() {
    return (
      <React.Fragment>
        <p>1</p>
        <p>2</p>
      </React.Fragment>); }}export default App;
Copy the code

Startup phase

The differences between the three startup modes are analyzed during the React application startup process. Before entering the React-Reconciler package (before calling updateContainer), the memory state diagram is as follows:

Based on this structure, you can print out the fiber tree corresponding to the current page in the console (to see its structure):

document.getElementById('root')._reactRootContainer._internalRoot.current;
Copy the code

Then go to the React-Reconciler package and call the updateContainer function:

/ /... Part of the code is omitted
export function updateContainer(element: ReactNodeList, container: OpaqueRoot, parentComponent: ? React$Component<any, any>, callback: ?Function.) :Lane {
  // Get the current timestamp
  const current = container.current;
  const eventTime = requestEventTime();
  // 1. Create a priority variable (lane model)
  const lane = requestUpdateLane(current);

  / / 2. According to priority lanes, create the update object, and add fiber. UpdateQueue. Pending queue
  const update = createUpdate(eventTime, lane);
  update.payload = { element };
  callback = callback === undefined ? null : callback;
  if(callback ! = =null) {
    update.callback = callback;
  }
  enqueueUpdate(current, update);

  // 3. Enter the 'input' section of the reconcier operation process
  scheduleUpdateOnFiber(current, lane, eventTime);
  return lane;
}
Copy the code

Due to theupdateObject, the memory structure is as follows:

Note: the original ReactElement object < App / > be mounted to HostRootFiber. UpdateQueue. Shared. Pending. Content. The element, later in the process of the fiber structure tree will change again.

Construction phase

In order to highlight the construction process and eliminate interference, FiberRoot and HostRootFiber in the memory state diagram are proposed separately (added later on the basis of them):

In scheduleUpdateOnFiber:

/ /... Omit part of the code
export function scheduleUpdateOnFiber(fiber: Fiber, lane: Lane, eventTime: number,) {
  // Mark the priority
  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
  if (lane === SyncLane) {
    if( (executionContext & LegacyUnbatchedContext) ! == NoContext && (executionContext & (RenderContext | CommitContext)) === NoContext ) {// For the first render, do the 'fiber construct' directly
      performSyncWorkOnRoot(root);
    }
    // ...}}Copy the code

As you can see, rendering, for the first time in Legacy mode and markUpdateLaneFromFiberToRoot and performSyncWorkOnRoot has two functions.

MarkUpdateLaneFromFiberToRoot fiber, lane () function in the fiber structure tree (in contrast to update) will play a role, because in the created for the first time and not with the current page of the fiber tree, so the core code does not perform, Finally, the FiberRoot object is returned directly.

PerformSyncWorkOnRoot looks like a lot of source code, the first time the real use of the two functions:

function performSyncWorkOnRoot(root) {
  let lanes;
  let exitStatus;
  if (
    root === workInProgressRoot &&
    includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)
  ) {
    // When first constructed (because root=fiberRoot, workInProgressRoot=null), it will not enter
  } else {
    }}}}}}}}}}}}}}}}}}}}}
    lanes = getNextLanes(root, NoLanes);
    // 2. Update from the root node to the bottom
    exitStatus = renderRootSync(root, lanes);
  }

  // Mount the latest fiber tree to the root.finishedWork node
  const finishedWork: Fiber = (root.current.alternate: any);
  root.finishedWork = finishedWork;
  root.finishedLanes = lanes;
  // Enter the COMMIT phase
  commitRoot(root);

  / /... The rest is not covered in this section
}
Copy the code

GetNextLanes returns the priority of the render (see the priority section of Fiber Tree Construction (Basic Preparation)).

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

In renderRootSync, the stack frame prepareFreshStack is refreshed before performing the Fiber tree construction (workLoopSync) (see Stack frame management in Fiber Tree Construction (Basic Preparation)). Here hostRootFiber. alternate is created and global variables workInProgress and workInProgressRoot are reset.

Loop structure

The logic goes to workLoopSync, which is compared to workLoopConcurrent, although this section discusses it in Legacy mode

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

You can see that workLoopConcurrent has an additional pause mechanism compared to Sync, which implements time slicing and interruptible rendering (see React scheduling principles).

Combine the performUnitOfWork function (source address)

/ /... 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

It is obvious that the whole Fiber tree is a depth-first traversal (refer to the React depth-first traversal), and there are two important variables workInProgress and current(refer to the double buffering technique introduced in the Fiber tree construction (basic preparation) above):

  • workInProgressandcurrentAre all Pointers
  • workInProgressPoints to what is currently under constructionfibernode
  • current = workInProgress.alternate(i.e.fiber.alternate) to which the page is currently in usefiberWhen the page is first constructed, it has not yet been renderedcurrent = null.

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

  1. Exploring stagebeginWork
  2. Back stagecompleteWork

These two stages together complete the creation of each fiber node, and all fiber nodes constitute the Fiber tree.

The search stage beginWork

BeginWork (Current, unitOfWork, subtreeRenderLanes)(source address) for all Fiber types, and each case processes one Fiber type. UpdateHostRoot, updateClassComponent, etc.)

  1. According to theReactElementObject to create allfiberNode, finally constructedFiber tree structure(setreturnandsiblingPointer)
  2. Set up thefiber.flags(Binary form variable, used to markfiberThe node’sAdd, delete, changeStatus, waitingThe completeWork phase is processed)
  3. Set up thefiber.stateNodeLocal states (e.gThe Class typeNodes:fiber.stateNode=new Class())
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  const updateLanes = workInProgress.lanes;
  if(current ! = =null) {
    // Update logic, render will not enter the first time
  } else {
    didReceiveUpdate = false;
  }
  // 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 // Keep only the case used in this example
  ) {
    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

UpdateXXX (such as: updateHostRoot, updateClassComponent, etc.) has many cases, but the main logic can be summarized as 3 steps:

  1. According to thefiber.pendingProps, fiber.updateQueueEtc.The input dataState, calculationfiber.memoizedStateAs aOutput state
  2. Access to the lowerReactElementobject
    1. The class typefibernode
      • buildReact.ComponentThe instance
      • Mount the new instance tofiber.stateNodeon
      • performrenderThe previous lifecycle function
      • performrenderMethod to get the subordinatereactElement
      • Set the parameters based on the actual situationfiber.flags
    2. The function typefibernode
      • Execute function to get the childreactElement
      • Set the parameters based on the actual situationfiber.flags
    3. The HostComponent type (e.g.div, span, button, etc.)fibernode
      • pendingProps.childrenAs a juniorreactElement
      • If the child node is a text node, set the child node to null. Prepare to entercompleteUnitOfWorkphase
      • Set the parameters based on the actual situationfiber.flags
    4. Other types…
  3. According to theReactElementObject, callreconcilerChildrengenerateFiberChild nodes (only generatedSecondary child node)
    • Set the parameters based on the actual situationfiber.flags

Different updateXXX functions handle different types of fiber nodes, with the overall purpose of generating child nodes down. In the process to mount need some persistent data in fiber node (such as fiber. StateNode, fiber memoizedState, etc.); Set the special operations of the fiber node to fiber.flags(e.g., node ref,class lifecycle,function hook, node delete, etc.).

UpdateHostRoot and updateHostComponent code are listed here. Analysis of other common cases (such as class and function types) is discussed in the status Components section.

The root node of fiber tree is HostRootFiber node, so the first time to enter beginWork will call updateHostRoot(Current, workInProgress, renderLanes).

// Omit code not relevant to this section
function updateHostRoot(current, workInProgress, renderLanes) {
  / / 1. State calculation, update integrated into workInProgress. MemoizedState
  const updateQueue = workInProgress.updateQueue;
  const nextProps = workInProgress.pendingProps;
  const prevState = workInProgress.memoizedState;
  constprevChildren = prevState ! = =null ? prevState.element : null;
  cloneUpdateQueue(current, workInProgress);
  / / traverse updateQueue. Shared. Pending, extract enough priority update object, calculate the final state workInProgress. MemoizedState
  processUpdateQueue(workInProgress, nextProps, null, renderLanes);
  const nextState = workInProgress.memoizedState;
  // 2. Obtain the subordinate 'ReactElement' object
  const nextChildren = nextState.element;
  const root: FiberRoot = workInProgress.stateNode;
  if (root.hydrate && enterHydrationState(workInProgress)) {
    / /... Server rendering related, omitted here
  } else {
    // 3. Use 'reconcilerChildren' to generate 'Fiber' child nodes based on the 'ReactElement' object from the ground up (only 'sub-nodes' are generated)
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  }
  return workInProgress.child;
}
Copy the code

Normal DOM tag types (such as div,span,p) will enter updateHostComponent:

/ /... Omit some irrelevant code
function updateHostComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  // there is no memoizedState for HostComponent. There is no memoizedState for HostComponent
  const type = workInProgress.type;
  const nextProps = workInProgress.pendingProps;
  constprevProps = current ! = =null ? current.memoizedProps : null;
  // 2. Obtain the subordinate 'ReactElement' object
  let nextChildren = nextProps.children;
  const isDirectTextChild = shouldSetTextContent(type, nextProps);

  if (isDirectTextChild) {
    // If the child node has only one text node, you don't need to create a fiber of type HostText
    nextChildren = null;
  } else if(prevProps ! = =null && shouldSetTextContent(type, prevProps)) {
    // Special operations need to set fiber.flags
    workInProgress.flags |= ContentReset;
  }
  // Special operations need to set fiber.flags
  markRef(current, workInProgress);
  // 3. Use 'reconcilerChildren' to generate 'Fiber' child nodes based on the 'ReactElement' object from the ground up (only 'sub-nodes' are generated)
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}
Copy the code

Backtrack phase completeWork

CompleteUnitOfWork (unitOfWork)(source address), process the beginWork phase has created the fiber node, the core logic:

  1. callcompleteWork
    • tofiberThe node (Tag =HostComponent, HostText) creates a DOM instance and sets itfiber.stateNodeLocal states (e.gtag=HostComponent, HostTextNode: fiber.statenode points to the DOM instance).
    • Set the properties for the DOM node, bind the event (here describes this step first, the detailed event processing process, inPrinciple of synthetic events).
    • Set up thefiber.flagstag
  2. The currentfiberObject’s side effect queue (firstEffectandlastEffect) is added to the parent node’s side effect queue to update the parent node’sfirstEffectandlastEffectPointer.
  3. identifybeginWorkstage-setfiber.flagsTo judge the currentfiberWhether there are side effects (add, delete, change), if so, need to be currentfiberAdded to the parent nodeeffectsQueue, waitcommitStage processing.
function completeUnitOfWork(unitOfWork: Fiber) :void {
  let completedWork = unitOfWork;
  // The outer loop controls and moves the pointer (' workInProgress ', 'completedWork', etc.)
  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;
    if ((completedWork.flags & Incomplete) === NoFlags) {
      let next;
      // 1. When processing the Fiber node, the renderer is called (call the React-DOM package, associate the Fiber node with the DOM object, bind the event, etc.).
      next = completeWork(current, completedWork, subtreeRenderLanes); // Process a single node
      if(next ! = =null) {
        // If other child nodes are derived, then go back to the 'beginWork' phase for processing
        workInProgress = next;
        return;
      }
      // Reset the priority of the child node
      resetChildLanes(completedWork);
      if( returnFiber ! = =null &&
        (returnFiber.flags & Incomplete) === NoFlags
      ) {
        // 2. Collect the side effects of the current Fiber node and its subtrees
        // 2.1 Add the child node's side effect queue to the parent node
        if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = completedWork.firstEffect;
        }
        if(completedWork.lastEffect ! = =null) {
          if(returnFiber.lastEffect ! = =null) {
            returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
          }
          returnFiber.lastEffect = completedWork.lastEffect;
        }
        // 2.2 If the current fiber node has side effects, add it to the child node's side effects queue.
        const flags = completedWork.flags;
        if (flags > PerformedWork) {
          // PerformedWork is read by React DevTools, so skip it
          if(returnFiber.lastEffect ! = =null) {
            returnFiber.lastEffect.nextEffect = completedWork;
          } else{ returnFiber.firstEffect = completedWork; } returnFiber.lastEffect = completedWork; }}}else {
      // Exception handling is not covered in this section
    }

    const siblingFiber = completedWork.sibling;
    if(siblingFiber ! = =null) {
      // If there are siblings, return and enter the 'beginWork' phase again
      workInProgress = siblingFiber;
      return;
    }
    // Move the pointer to the next node
    completedWork = returnFiber;
    workInProgress = completedWork;
  } while(completedWork ! = =null);
  / / has been back to the root node, set workInProgressRootExitStatus = RootCompleted
  if(workInProgressRootExitStatus === RootIncomplete) { workInProgressRootExitStatus = RootCompleted; }}Copy the code

Next, analyze the fiber handler function completeWork

function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) :Fiber | null {
  const newProps = workInProgress.pendingProps;
  switch (workInProgress.tag) {
    case ClassComponent: {
      // The Class type is not processed
      return null;
    }
    case HostRoot: {
      const fiberRoot = (workInProgress.stateNode: FiberRoot);
      if (fiberRoot.pendingContext) {
        fiberRoot.context = fiberRoot.pendingContext;
        fiberRoot.pendingContext = null;
      }
      if (current === null || current.child === null) {
         // Set the fiber. Flags flag
         workInProgress.flags |= Snapshot;
      }
      return null;
    }
    case HostComponent: {
      popHostContext(workInProgress);
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      if(current ! = =null&& workInProgress.stateNode ! =null) {
        // Update logic, render will not enter the first time
      } else {
        const currentHostContext = getHostContext();
        // 1. Create DOM objects
        const instance = createInstance(
          type,
          newProps,
          rootContainerInstance,
          currentHostContext,
          workInProgress,
        );
        // 2. Append the DOM object in the subtree to the DOM object in this node
        appendAllChildren(instance, workInProgress, false.false);
        // Set the stateNode property to point to the DOM object
        workInProgress.stateNode = instance;
        if (
          // 3. Set the properties of the DOM object, bind events, etc
          finalizeInitialChildren(
            instance,
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
          )
        ) {
          // Set fibre. flags (Update)
          markUpdate(workInProgress);
        }
        if(workInProgress.ref ! = =null) {
          // Set fibre.flags (Ref)
          markRef(workInProgress);
        }
        return null; }}}Copy the code

It can be seen that fiber.flags will also be set when the conditions are met, so the setting of fiber.flags is not only in the beginWork phase.

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:workInProgressPointer toHostRootFiber.alternateObject, at which pointcurrent = workInProgress.alternatePoint to thefiberRoot.currentNon-empty (first construct, only at root node,currentNot empty).
  • Execution process: callupdateHostRoot
    • inreconcilerChildrenStage, downward constructionSubnode fiber(<App/>), and set the child node (fiber(<App/>))fiber.flags |= Placement
  • After execution: Returns the lower-level nodefiber(<App/>), the mobileworkInProgressThe pointer points to the child nodefiber(<App/>)

PerformUnitOfWork second call (beginWork only):

  • Perform before:workInProgressPointer tofiber(<App/>)Node, wherecurrent = null
  • Execution process: callupdateClassComponent
    • In this example, the class instance has a lifecycle functioncomponentDidMount, so it will be setfiber(<App/>)nodeworkInProgress.flags |= Update
    • Also for the sake ofReact DevToolsCan identify the status of the component’s execution progress, will setworkInProgress.flags |= PerformedWork(in thecommitThe phase will rule that outflag, listed here onlyworkInProgress.flagsThe setup scenario is not discussedReact DevTools)
    • Need to pay attention toclassInstance.render()After this step, although the returnrenderMethodReactElementObject, but thenreconcilerChildrenOnly structureSecondary child node
    • inreconcilerChildrenStage, downward constructionSecondary child node div
  • After execution: Returns the lower-level nodefiber(div), the mobileworkInProgressThe pointer points to the child nodefiber(div)

PerformUnitOfWork the third call (beginWork only):

  • Perform before:workInProgressPointer tofiber(div)Node, wherecurrent = null
  • Execution process: callupdateHostComponent
    • inreconcilerChildrenStage, downward constructionSecondary child node(In this example,divThere are two secondary children)
  • After execution: Returns the lower-level nodefiber(header), the mobileworkInProgressThe pointer points to the child nodefiber(header)

PerformUnitOfWork fourth call (perform beginWork and completeUnitOfWork):

  • beginWorkPerform before:workInProgressPointer tofiber(header)Node, wherecurrent = null
  • beginWorkExecution process: callupdateHostComponent
    • In this exampleheaderThe 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
  • beginWorkAfter execution: Because the lower-level node isnull, so entercompleteUnitOfWork(unitOfWork)Function, the parameters passed inunitOfWorkIt’s essentiallyworkInProgress(This point points tofiber(header)Node)

  • completeUnitOfWorkPerform before:workInProgressPointer tofiber(header)node
  • completeUnitOfWorkImplementation process: tofiber(header)For the starting point, back up

The first cycle:

  1. performcompleteWorkfunction
    • createfiber(header)Node correspondingDOMInstance andappendThe child nodes of theDOMThe instance
    • Set up theDOMProperties, binding events, etc. (in this case, nodefiber(header)No event binding)
  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, theworkInProgressThe pointer points to the next siblingfiber(<Content/>)To quitcompleteUnitOfWork.

PerformUnitOfWork the fifth call (beginWork):

  • Perform before:workInProgressPointer tofiber(<Content/>)Node.
  • Implementation process: This is aclassType, consistent with the second call logic.
  • After execution: Returns the lower-level nodefiber(p), the mobileworkInProgressThe pointer points to the child nodefiber(p)

PerformUnitOfWork 6th call (perform beginWork and completeUnitOfWork): same logic as the 4th call to create the fiber(header) node. BeginWork and completeUnitOfWork will be executed, and finally the DOM instance will be constructed, and the workInProgress pointer will be pointed to the next sibling node fiber(p).

PerformUnitOfWork the seventh call (perform beginWork and completeUnitOfWork):

  • beginWorkExecution procedure: Created in the last callfiber(p)The logic of the nodes is consistent
  • completeUnitOfWorkImplementation process: tofiber(p)For the starting point, back up

The first cycle:

  1. performcompleteWorkFunction: createfiber(p)Node correspondingDOMInstance andappendSubtree nodeDOMThe instance
  2. Move up side queue: due to this nodefiber(p)There are no side effects, so the side effects queue does not change substantially after execution (it is currently empty).
  3. Backtracking upwards: since there is no sibling node, theworkInProgressThe pointer points to the parentfiber(<Content/>)

The second cycle:

  1. performcompleteWorkFunction: Nodes of type class are not processed
  2. Moving up the side effect queue:
    • This nodefiber(<Content/>)theflagsFlag bit changed (completedWork.flags > PerformedWork) to add this node to the parent node (fiber(div)) after the side effects queue (firstEffectandlastEffectProperty points to the head and tail of the side effect queue, respectively.
  3. Back up: putworkInProgressThe pointer points to the parentfiber(div)

The third cycle:

  1. performcompleteWorkFunction: createfiber(div)Node correspondingDOMInstance andappendSubtree nodeDOMThe instance
  2. Moving up the side effect queue:
    • This nodefiber(div)The side effect queue is not empty, splicing it to the parent nodefiber<App/>Side effects queue.
  3. Back up: putworkInProgressThe pointer points to the parentfiber(<App/>)

The fourth cycle:

  1. performcompleteWorkFunction: Nodes of type class are not processed
  2. Moving up the side effect queue:
    • This nodefiber(<App/>)The side effect queue is not empty, splicing it to the parent nodefiber(HostRootFiber)Side effects on the queue.
    • This nodefiber(<App/>)theflagsFlag bit changed (completedWork.flags > PerformedWork) to add this node to the parent nodefiber(HostRootFiber)The side effects queue after.
    • The order of the last queue isThe child node is first, and the local node is second
  3. Back up: putworkInProgressThe pointer points to the parentfiber(HostRootFiber)

The fifth cycle:

  1. performcompleteWorkFunction: forHostRootType of node, set when first constructedworkInProgress.flags |= Snapshot
  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

At this point, the entire fiber tree construction loop has been executed, with a complete fiber tree and side effect queues mounted on the root node of the fiber tree. The further down the hierarchy, the higher the child nodes are.

RenderRootSync exits before resetting workInProgressRoot = null to indicate that 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):

conclusion

This section demonstrates the entire process of creating the fiber tree for the first time, tracking the memory reference changes during the process. The Fiber tree construction loop is responsible for constructing the new fiber tree, marking the fiber. Flags during the construction, and finally collecting all the flagged Fiber nodes into a side effect queue. The side effects of the queue was mounted to the root node (HostRootFiber. Alternate. FirstEffect). At this point, the Fiber tree and its DOM nodes are still in memory, waiting for the commitRoot phase to render.

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.