React Source code

  • React source code: Fiber structure
  • React Fiber rendering (1)
  • React source code for Fiber rendering (2) beginWork
  • React source code parsing Fiber rendering (3) final

completeWork

Compared to the beginWork stage, the completeWork stage, it’s easier to deal with. Similarly, for completeWork, the workinprogress. tag is used to determine which type logic to follow.

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:
      bubbleProperties(workInProgress);
      return null;
    case ClassComponent: {
      const Component = workInProgress.type;
      if (isLegacyContextProvider(Component)) {
        popLegacyContext(workInProgress);
      }
      bubbleProperties(workInProgress);
      return null;
    }
Copy the code

For the completeWork stage, there are a lot of types that don’t need to be handled, emptying the context stack, and handling some lanes. So, we’ll focus on the HostText and HostComponent types.

HostText

case HostText: {
      const newText = newProps;
      // Check whether the status is update.
      if(current && workInProgress.stateNode ! =null) {
        const oldText = current.memoizedProps;
        // Pass the Text type to workinprogress.statenode
        updateHostText(current, workInProgress, oldText, newText);
      } else {
        if (typeofnewText ! = ='string') {
          / /...
        }
        
        const rootContainerInstance = getRootHostContainer();
        const currentHostContext = getHostContext();
        / /...
        if (wasHydrated) {
          / /...
        } else {
          // Through the TextNode of createTextNodeworkInProgress.stateNode = createTextInstance( newText, rootContainerInstance, currentHostContext, workInProgress, ); }}return null;
    }
Copy the code

For the HostText type, current && workinProgress.statenode! = null condition to distinguish what phase the current node is in.

True if you are in the UPDATE phase: mark the update (workInProgress) and wait until you are in the COMMIT phase.

If the value of createTextNode is mounted (false), return workinProgress.statenode to workInProgress.statenode.

HostComponent

HostComponent is a native DOM component. == null && workInProgress.stateNode ! Mounted: False and update: true are distinguished.

Update:

Call updateHostComponent if current.ref! == workinprogress. ref = true, mark markRef(workInProgress).

For the updateHostComponent function, reuse workinprogress. stateNode as instance.

instance: Instance = workInProgress.stateNode;
Copy the code

Call prepareUpdate and return updateQueue.

export function prepareUpdate(domElement: Instance, type: string, oldProps: Props, newProps: Props, rootContainerInstance: Container, hostContext: HostContext,) :null | Array<mixed> {
  // Subtract the attribute
  return diffProperties(
    domElement,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
  );
}
Copy the code

CommitRoot phase

Here’s a quick word about the commitRoot phase.

For a commitRoot phase, there are three phases: before mutation, mutation, and layout.

Before mutation:

Initialize the empty, empty the side effects, and call commitRootImpl. And then reset the state

  // ...
  // Clear the callback
  root.callbackNode = null;
  root.callbackPriority = NoLanePriority;

  / / merge lane
  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
  // Reset the priority
  markRootFinished(root, remainingLanes);
    // Reset global variables
  if (root === workInProgressRoot) {
    / / in
    workInProgressRoot = null;
    workInProgress = null;
    workInProgressRootRenderLanes = NoLanes;
  }
 / /...
/ / scheduling useEffect
if((effectTag & Passive) ! == NoEffect) {if(! rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects =true;
    scheduleCallback(NormalSchedulerPriority, () = > {
      flushPassiveEffects();
      return null;
    });
  }
}
nextEffect = nextEffect.nextEffect;
Copy the code

Call commitBeforeMutationEffects function, judge whether getSnapshotBeforeUpdate classComponent implementation life cycle

The return of this function tocomponentDidUpdate

commitBeforeMutationEffects
// 
export function commitBeforeMutationEffects(root: FiberRoot, firstChild: Fiber,) {
    / /...
  nextEffect = firstChild;
  commitBeforeMutationEffects_begin();
 / /...
  return shouldFire;
}
// 
function commitBeforeMutationEffects_begin() {
  while(nextEffect ! = =null) {
    const fiber = nextEffect;
    / /...
    const child = fiber.child;
    if( (fiber.subtreeFlags & BeforeMutationMask) ! == NoFlags && child ! = =null
    ) {
      / /...
      nextEffect = child;
    } else {
    // Before Mutation processing is completecommitBeforeMutationEffects_complete(); }}}function commitBeforeMutationEffects_complete() {
  while(nextEffect ! = =null) {
    const fiber = nextEffect;
    if (__DEV__) {
      / /...
    } else {
      try {
      // Handle side effects
        commitBeforeMutationEffectsOnFiber(fiber);
      } catch (error) {
        / /...}}// Process sibling nodes
    const sibling = fiber.sibling;
    if(sibling ! = =null) {
      nextEffect = sibling;
      return; } nextEffect = fiber.return; }}function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
  const current = finishedWork.alternate;
  const flags = finishedWork.flags;
  // ...

  if((flags & Snapshot) ! == NoFlags) { setCurrentDebugFiberInDEV(finishedWork);switch (finishedWork.tag) {
      case FunctionComponent:
      case ForwardRef:
      case SimpleMemoComponent: {
        break;
      }
      case ClassComponent: {
        if(current ! = =null) {
          const prevProps = current.memoizedProps;
          const prevState = current.memoizedState;
          const instance = finishedWork.stateNode;
          // Call lifecycle
          const snapshot = instance.getSnapshotBeforeUpdate(
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );
          // Cache the return value of the processing
          instance.__reactInternalSnapshotBeforeUpdate = snapshot;
        }
        break;
      }
Copy the code

mutation

Enter the mutation stage and call commitMutationEffects(root, finishedWork). function

commitMutationEffects

export function commitMutationEffects(root: FiberRoot, firstChild: Fiber) {
  nextEffect = firstChild;
  commitMutationEffects_begin(root);
}

/ /... Call commitMutationEffectsOnFiber processing function type
commitMutationEffectsOnFiber(fiber, root);
/ /...

function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
  const flags = finishedWork.flags;

  // What is the status of the current component according to primaryFlag
  const primaryFlags = flags & (Placement | Update | Hydrating);
  outer: switch (primaryFlags) {
    / /... Processing commitWork
    case Update: {
      const current = finishedWork.alternate;
      commitWork(current, finishedWork);
      break; }}}Copy the code

Layout stage

When this stage is reached, the DOM has been rendered, indicating that the mutation stage is complete. At this stage, the call lifecycle and hooks are already accessible to DOM elements.

commitLayoutEffects(finishedWork, root, lanes);
                    |
commitLayoutEffects_begin(finishedWork, root, committedLanes);
                    |
commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
                    |
commitLayoutEffectOnFiber(root, current, fiber, committedLanes);
Copy the code
function commitLayoutEffectOnFiber(
  finishedRoot: FiberRoot,
  current: Fiber | null,
  finishedWork: Fiber,
  committedLanes: Lanes,
) :void {
  if((finishedWork.flags & (Update | Callback)) ! == NoFlags) {switch (finishedWork.tag) {
      // These are FunctionComponent and related types
        case FunctionComponent:
        case ForwardRef:
        case SimpleMemoComponent:
        case Block: {
          // Execute the useLayoutEffect callback function
          commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
          // Schedule useEffect destruction and callback functions
          schedulePassiveEffects(finishedWork);
          return;
        }
      case ClassComponent: {
        const instance = finishedWork.stateNode;

        if (finishedWork.flags & Update) {
            / /...
            // The component completes the call lifecycle
              instance.componentDidMount();
          } else {
            const prevProps =
              finishedWork.elementType === finishedWork.type
                ? current.memoizedProps
                : resolveDefaultProps(finishedWork.type, current.memoizedProps);
            const prevState = current.memoizedState;
             / /...
             // Calling the componentDidUpdate life cycle passes in the return value of getSnapshotBeforeUpdateinstance.componentDidUpdate( prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate, ); }}// Update the queue
          commitUpdateQueue(finishedWork, updateQueue, instance);
        }
        break;
      }
 / /...
Copy the code

For FunctionComponent component, called commitHookEffectListMount function performs side effects.

useEffect
function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
  constlastEffect = updateQueue ! = =null ? updateQueue.lastEffect : null;
  if(lastEffect ! = =null) {
    const firstEffect = lastEffect.next;
    let effect = firstEffect;
    do {
      if ((effect.tag & tag) === tag) {
        // Mount 
        const create = effect.create;
        effect.destroy = create();
      effect = effect.next;
    } while (effect !== firstEffect);
  }
}
Copy the code

For classComponent, finishedWork.flags & Update, mount componentDidMount, During the update call instance.com ponentDidUpdate function.

Finally, call the commitUpdateQueue function, process the effect function, and call after the call.

export function commitUpdateQueue<State> (finishedWork: Fiber, finishedQueue: UpdateQueue
       
        , instance: any,
       ) :void {
  // Commit the effects
  const effects = finishedQueue.effects;
  finishedQueue.effects = null;
  if(effects ! = =null) {
    effects.forEach(effect= > {
      const callback = effect.callback;
      if(callback ! = =null) {
        effect.callback = null; callCallback(callback, instance); }})Copy the code

CommitUpdateQueue primarily addresses setState callbacks.

The end of the

These articles mainly introduce the basic flow of React. Render.