When an update is generated, the Fiber node of workInProgressTree is generated in two ways:

  1. re-render
  2. reusecurrentTreeFibernode

The following research was conducted in this paper:

Can you reuse the Fiber node of currentTree when updating?

beginWork

When scheduling update, it will enter the render stage, which is the stage of Fiber generation, at this time, the beginWork method will be called, in which the processing of class components and function components is as follows:

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
){
    switch (workInProgress.tag) {
      case FunctionComponent: {
        const Component = workInProgress.type;
        const unresolvedProps = workInProgress.pendingProps;
        const resolvedProps =
          workInProgress.elementType === Component
            ? unresolvedProps
            : resolveDefaultProps(Component, unresolvedProps);
        return updateFunctionComponent(
          current,
          workInProgress,
          Component,
          resolvedProps,
          renderLanes,
        );
      }
      case ClassComponent: {
        const Component = workInProgress.type;
        const unresolvedProps = workInProgress.pendingProps;
        const resolvedProps =
          workInProgress.elementType === Component
            ? unresolvedProps
            : resolveDefaultProps(Component, unresolvedProps);
        returnupdateClassComponent( current, workInProgress, Component, resolvedProps, renderLanes, ); }}}Copy the code

Let’s look at ClassComponent generating Fiber first and then FunctionComponent.

ClassComponent

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
    
    const nextUnitOfWork = finishClassComponent(
      current,
      workInProgress,
      Component,
      shouldUpdate,
      hasContext,
      renderLanes,
    );
    return nextUnitOfWork;
  }
Copy the code

The finishClassComponent method returns the Fiber node, and whether current Fiber can be reused depends on the updateClassInstance method.

Take a look at updateClassInstance:

function updateClassInstance(current: Fiber, workInProgress: Fiber, ctor: any, newProps: any, renderLanes: Lanes,) {
      const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState,
      nextContext,
    );
    return shouldUpdate;
  }
Copy the code

UpdateClassInstance = updateClassInstance ();

  1. Is there aforceUpdate
  2. shouldComponentUpdatecheckShouldComponentUpdate)

If Fiber is reusable for ClassComponent, it depends on the props () method if this update is generated by calling forceUpdate.

function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext,) {
  const instance = workInProgress.stateNode;
  if (typeof instance.shouldComponentUpdate === 'function') {
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextContext,
    );

    return shouldUpdate;
  }

  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return(! shallowEqual(oldProps, newProps) || ! shallowEqual(oldState, newState) ); }return true;
}
Copy the code

According to the return of checkShouldComponentUpdate, when there is no set shouldComponentUpdate method, the effect is equivalent to set the shouldComponentUpdate but constant returns true, The ClassComponent is re-render by various factors.

Let’s see how the Functioncomponent generates Fiber nodes.

FunctionComponent

function updateFunctionComponent(current, workInProgress, Component, nextProps: any, renderLanes,) {
    if(current ! = =null && !didReceiveUpdate) {
      bailoutHooks(current, workInProgress, renderLanes);
      returnbailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); }}Copy the code

Whether the FunctionComponent is Fiber reusable depends on the didReceiveUpdate variable, which is assigned to the beginWork method:

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
){
    if( oldProps ! == newProps || hasLegacyContextChanged() || (__DEV__ ? workInProgress.type ! == current.type :false)
    ) {
      didReceiveUpdate = true;
    } else if(! includesSomeLane(renderLanes, updateLanes)) {// Enter this judgment when the priority is not enough
      didReceiveUpdate = false;
      switch(workInProgress.tag) {
            // ...
        } 
        // It is possible to execute this if the switch above does not have a return statement
        return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    } else {
      if((current.flags & ForceUpdateForLegacySuspense) ! == NoFlags) { didReceiveUpdate =true;
      } else {
        didReceiveUpdate = false; }}}Copy the code

The didReceiveUpdate variable is set to false when the FunctionComponent props, context (dev) is unchanged and the update priority is sufficient. The reusable Fiber node is returned after the subsequent execution of the updateFunctionComponent method.

conclusion

Whether workInProgressTree can reuse the Fiber node of currentTree when updating depends on:

  1. ClassComponent
    1. This update is not a callforceUpdateTo update the
    2. shouldComponentUpdateTo determine that the update does not need to be updated
  2. FunctionComponent
    1. devIs the element typetypeThe same
    2. propsThe same
    3. contextThere is no update
    4. This time the priority is enough

reference

[beginWork/updateFunctionComponent/updateClassComponent/finishClassComponent] github.com/facebook/re…

UpdateClassInstance/checkShouldComponentUpdate github.com/facebook/re…

[checkHasForceUpdateAfterProcessing] github.com/facebook/re…