Although function components are now used, class components still have some advantages that function components cannot replace, such as some unique life cycles, the convenience of storing variables without requiring a lot of useRef, and the convenience of instance method calls.

The React version is 17.0.1.

Class component Fiber node creation

The Fiber node the components of a parent node after entering reconcileChildren method through createFiberFromTypeAndProps method to create, In this method through the prototype. IsReactComponent to judge whether the node for ClassComponent.

export function createFiberFromTypeAndProps(
  type: any, // React$ElementType
  key: null | string,
  pendingProps: any,
  owner: null | Fiber,
  mode: TypeOfMode,
  lanes: Lanes,
) :Fiber {
  // omit irrelevant code...
  let fiberTag = IndeterminateComponent;
  if (typeof type === 'function') {
    / / by prototype. IsReactComponent distinguish the type of component
    if (shouldConstruct(type)) {
      // Mark it as a class componentfiberTag = ClassComponent; }}// omit irrelevant code...
  return fiber;
}

/ / by prototype. IsReactComponent distinguish the type of component
function shouldConstruct(Component: Function) {
  const prototype = Component.prototype;
  return!!!!! (prototype && prototype.isReactComponent); }Copy the code

Due to the need to inherit the Component class components, all existing prototype. IsReactComponent, the node is marked as class components, follow-up will enter class components.

Class component instance creation and update

The Fiber node is marked as a ClassComponent in the previous step, and when the node enters the beginWork phase it enters the updateClassComponent method by judgment.

/ /... Omit irrelevant code
case ClassComponent: {
  // Class component type is the class itself, not an instance of the class
  const Component = workInProgress.type;
  const unresolvedProps = workInProgress.pendingProps;
  / / defaultProps merger
  const resolvedProps =
    workInProgress.elementType === Component
      ? unresolvedProps
      : resolveDefaultProps(Component, unresolvedProps);
  return updateClassComponent(
    current,
    workInProgress,
    Component,
    resolvedProps,
    renderLanes,
  );
}
/ /... Omit irrelevant code
Copy the code

Whenever a class component is first mounted or updated, it goes to updateClassComponent.

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {
  // omit irrelevant code...
    
  / / class instance
  const instance = workInProgress.stateNode;
  let shouldUpdate;
  // mount
  if (instance === null) {
    // omit irrelevant code...
    // Create and initialize the class instance
    constructClassInstance(workInProgress, Component, nextProps);
    mountClassInstance(workInProgress, Component, nextProps, renderLanes);
    shouldUpdate = true;
  } else if (current === null) {
    // Continue rendering after interrupt...
  } else {
    // update
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  }
  // Return to the subfiber node
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderLanes,
  );

  return nextUnitOfWork;
}
Copy the code

Mount and Update are judged separately in the updateClassComponent.

When the Mount

Mount creates an instance, assigns an updater(i.e., setState, forceUpdate, etc.), and initializes updateQueue. The main methods called are constructClassInstance and mountClassInstance.

constructClassInstance

The constructClassInstance method creates an instance of the class and binds it to the Fiber node, assigning the updater.

function constructClassInstance(workInProgress: Fiber, ctor: any, props: any,) :any {
  let isLegacyContextConsumer = false;
  let unmaskedContext = emptyContextObject;
  let context = emptyContextObject;
  // omit context-related code...

  // Create an instance
  const instance = new ctor(props, context);
  // Assign state to memoizedState of the Fiber node
  conststate = (workInProgress.memoizedState = instance.state ! = =null&& instance.state ! = =undefined
      ? instance.state
      : null);
  / / assignment updater
  adoptClassInstance(workInProgress, instance);
    
  // omit irrelevant code...
    
  return instance;
}

function adoptClassInstance(workInProgress: Fiber, instance: any) :void {
  / / assignment updater
  instance.updater = classComponentUpdater;
  workInProgress.stateNode = instance;
  // Access the fiber node through the instance._reactInternals property
  setInstance(instance, workInProgress);
}
Copy the code

mountClassInstance

The mountClassInstance method assigns props and state to the class instance, initializes updateQueue, and executes the getDerivedStateFromProps lifecycle to obtain the new state. Also execute the componentWillMount life cycle as judged.

The subsequent value of state is updated by performing update in updateQueue.

function mountClassInstance(workInProgress: Fiber, ctor: any, newProps: any, renderLanes: Lanes,) :void {
  / / class instance
  const instance = workInProgress.stateNode;
  / / assignment props, the state
  instance.props = newProps;
  instance.state = workInProgress.memoizedState;
  instance.refs = emptyRefsObject;
  // Initialize UpdateQueue
  initializeUpdateQueue(workInProgress);

  const contextType = ctor.contextType;
  if (typeof contextType === 'object'&& contextType ! = =null) {
    // Attach the context to the instance context
    instance.context = readContext(contextType);
  }

  // execute updateQueue to calculate the new state
  processUpdateQueue(workInProgress, newProps, instance, renderLanes);
  instance.state = workInProgress.memoizedState;

  const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  // Execute getDerivedStateFromProps to assign to state
  if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    );
    instance.state = workInProgress.memoizedState;
  }

	/ / call componentWillMount
  if (
    typeofctor.getDerivedStateFromProps ! = ='function' &&
    typeofinstance.getSnapshotBeforeUpdate ! = ='function' &&
    (typeof instance.UNSAFE_componentWillMount === 'function' ||
      typeof instance.componentWillMount === 'function')) {/ / call UNSAFE_componentWillMount
    callComponentWillMount(workInProgress, instance);
    // Execute updateQueue to handle the update initiated by UNSAFE_componentWillMount
    processUpdateQueue(workInProgress, newProps, instance, renderLanes);
    // Assign a new state
    instance.state = workInProgress.memoizedState;
  }

  // Flags increment Update with componentDidMount
  if (typeof instance.componentDidMount === 'function') { workInProgress.flags |= Update; }}Copy the code

When the Update

Update calls updateClassInstance to handle updates.

updateClassInstance

function updateClassInstance(current: Fiber, workInProgress: Fiber, ctor: any, newProps: any, renderLanes: Lanes,) :boolean {
  / / class instance
  const instance = workInProgress.stateNode;
  // Clone the current update ue to workInProgress
  cloneUpdateQueue(current, workInProgress);

  // When Fiber completes the beginWork phase, memoizedProps is assigned to pendingProps
  const unresolvedOldProps = workInProgress.memoizedProps;
  const oldProps =
    workInProgress.type === workInProgress.elementType
      ? unresolvedOldProps
      : resolveDefaultProps(workInProgress.type, unresolvedOldProps);
  instance.props = oldProps;
  const unresolvedNewProps = workInProgress.pendingProps;

  // context
  const oldContext = instance.context;
  const contextType = ctor.contextType;
  let nextContext = emptyContextObject;
  if (typeof contextType === 'object'&& contextType ! = =null) {
    nextContext = readContext(contextType);
  } // ...

  const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
  // Whether a new life cycle is used
  const hasNewLifecycles =
    typeof getDerivedStateFromProps === 'function' ||
    typeof instance.getSnapshotBeforeUpdate === 'function';

  / / call UNSAFE_componentWillReceiveProps
  if (
    !hasNewLifecycles &&
    (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
      typeof instance.componentWillReceiveProps === 'function')) {if( unresolvedOldProps ! == unresolvedNewProps || oldContext ! == nextContext ) { callComponentWillReceiveProps( workInProgress, instance, newProps, nextContext, ); }}// Reset the forceUpdate identifier before executing updateQueue
  resetHasForceUpdateBeforeProcessing();

  const oldState = workInProgress.memoizedState;
  let newState = (instance.state = oldState);
  // Execute updateQueue to obtain the new state
  processUpdateQueue(workInProgress, newProps, instance, renderLanes);
  newState = workInProgress.memoizedState;

  // Do not understand this paragraph
  if( unresolvedOldProps === unresolvedNewProps && oldState === newState && ! hasContextChanged() && ! checkHasForceUpdateAfterProcessing() ) {if (typeof instance.componentDidUpdate === 'function') {
      if( unresolvedOldProps ! == current.memoizedProps || oldState ! == current.memoizedState ) { workInProgress.flags |= Update; }}if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      if( unresolvedOldProps ! == current.memoizedProps || oldState ! == current.memoizedState ) { workInProgress.flags |= Snapshot; }}return false;
  }

  / / getDerivedStateFromProps execution
  if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    );
    newState = workInProgress.memoizedState;
  }

  // Use the forceUpdate flag and shouldComponentUpdate to determine whether an update is needed
  const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState,
      nextContext,
    );

  // Update required
  if (shouldUpdate) {
    // Call the UNSAFE_componentWillUpdate life cycle
    if (
      !hasNewLifecycles &&
      (typeof instance.UNSAFE_componentWillUpdate === 'function' ||
        typeof instance.componentWillUpdate === 'function')) {if (typeof instance.componentWillUpdate === 'function') {
        instance.componentWillUpdate(newProps, newState, nextContext);
      }
      if (typeof instance.UNSAFE_componentWillUpdate === 'function') { instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext); }}if (typeof instance.componentDidUpdate === 'function') {
      workInProgress.flags |= Update;
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') { workInProgress.flags |= Snapshot; }}else {
    // Do not understand this paragraph
    if (typeof instance.componentDidUpdate === 'function') {
      if( unresolvedOldProps ! == current.memoizedProps || oldState ! == current.memoizedState ) { workInProgress.flags |= Update; }}if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      if( unresolvedOldProps ! == current.memoizedProps || oldState ! == current.memoizedState ) { workInProgress.flags |= Snapshot; } } workInProgress.memoizedProps = newProps; workInProgress.memoizedState = newState; }// Update the props,state,context of the instance
  instance.props = newProps;
  instance.state = newState;
  instance.context = nextContext;

  return shouldUpdate;
}
Copy the code

finishClassComponent

Both Mount and Update require the finishClassComponent method, which returns the Child node after the reconcileChildren is processed.

function finishClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  shouldUpdate: boolean,
  hasContext: boolean,
  renderLanes: Lanes,
) {
  // The tag needs to be assigned ref
  markRef(current, workInProgress);

  constdidCaptureError = (workInProgress.flags & DidCapture) ! == NoFlags;// No update required
  if(! shouldUpdate && ! didCaptureError) {return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }

  const instance = workInProgress.stateNode;

  let nextChildren;
  // There was an error and getDerivedStateFromError was not declared
  if (
    didCaptureError &&
    typeofComponent.getDerivedStateFromError ! = ='function'
  ) {
    // Leave children unrendered and leave it to componentDidCatch
    nextChildren = null;
  } else {
    // Execute render to get children
    nextChildren = instance.render();
  }

  if(current ! = =null && didCaptureError) {
    forceUnmountCurrentAndReconcile(
      current,
      workInProgress,
      nextChildren,
      renderLanes,
    );
  } else {
    / / deal with children
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  }

  workInProgress.memoizedState = instance.state;

  return workInProgress.child;
}
Copy the code

Class component update scheduling

Class updates exist in the updateQueue on the Fiber node, which stores Update information. Class components can create updates using setState and add them to the updateQueue to initiate Update scheduling. The processUpdateQueue method is called to calculate the new state when the updateClassInstance method is executed during Update.

Structure of Update Ue and Update

When the mountClassInstance method is executed while the class component is mounted, the initializeUpdateQueue method is internally called to initialize the updateQueue.

export function initializeUpdateQueue<State> (fiber: Fiber) :void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState, // Update calculates the new state based on baseState
    firstBaseUpdate: null.// Priority dependent, skipped update
    lastBaseUpdate: null.// Priority dependent, skipped update
    shared: {
      pending: null.// The current update list
    },
    effects: null.// Update callback, corresponding to setState second parameter
  };
  fiber.updateQueue = queue; / / initialization
}
Copy the code

The createUpdate method is called by executing setState to create the update and add it to updateQueue. Update is a circular list, and the next field points to the next update.

export function createUpdate(eventTime: number, lane: Lane) :Update< * >{
  const update: Update<*> = {
    eventTime,
    lane, // Priority is related

    tag: UpdateState, // Update the state value according to the tag judgment
    payload: null.// corresponds to the first argument of setState
    callback: null.// corresponds to the second argument of setState

    next: null.// Because it is a linked list, it points to the next update
  };
  return update;
}
Copy the code

processUpdateQueue

The processUpdateQueue method is called when the class component is updated to get the new state.

/ / updateQueue execution
export function processUpdateQueue<State> (workInProgress: Fiber, props: any, instance: any, renderLanes: Lanes,) :void {
  // Never null in ClassComponent
  const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);

  // The global identifier determines whether the update was initiated by forceUpdate
  hasForceUpdate = false;

  // Update list skipped because priority was last updated
  let firstBaseUpdate = queue.firstBaseUpdate;
  let lastBaseUpdate = queue.lastBaseUpdate;

  // If there are pending updates, splice them to baseUpdate
  let pendingQueue = queue.shared.pending;
  if(pendingQueue ! = =null) {
    queue.shared.pending = null;
    // Disconnect the last one from the first one so that the pendingQueue is not a loop
    const lastPendingUpdate = pendingQueue;
    const firstPendingUpdate = lastPendingUpdate.next;
    lastPendingUpdate.next = null;
    // Connect pending updates to the Base Queue
    if (lastBaseUpdate === null) {
      firstBaseUpdate = firstPendingUpdate;
    } else {
      lastBaseUpdate.next = firstPendingUpdate;
    }
    lastBaseUpdate = lastPendingUpdate;

    // Ensure that current and workInProgress are consistent
    const current = workInProgress.alternate;
    if(current ! = =null) {
      // omit code...}}if(firstBaseUpdate ! = =null) {
    let newState = queue.baseState;
    let newLanes = NoLanes;

    let newBaseState = null;
    let newFirstBaseUpdate = null;
    let newLastBaseUpdate = null;

    let update = firstBaseUpdate;
    do {
      const updateLane = update.lane;
      const updateEventTime = update.eventTime;
      // Tasks with insufficient priority
      if(! isSubsetOfLanes(renderLanes, updateLane)) {// clone skipped Update
        const clone: Update<State> = {
          eventTime: updateEventTime,
          lane: updateLane,

          tag: update.tag,
          payload: update.payload,
          callback: update.callback,

          next: null};// Connect to firstBaseUpdate or lastBaseUpdate
        if (newLastBaseUpdate === null) {
          newFirstBaseUpdate = newLastBaseUpdate = clone;
          // The state value of the skipped Update will be used next time
          newBaseState = newState;
        } else {
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }
        newLanes = mergeLanes(newLanes, updateLane);
      } else {
        // The priority of the Update cannot be skipped

        // If a previous update was skipped, add laseBaseUpdate to ensure continuity for the next update
        if(newLastBaseUpdate ! = =null) {
          const clone: Update<State> = {
            eventTime: updateEventTime,
            // NoLane will not be skipped the next time it is executed
            lane: NoLane,

            tag: update.tag,
            payload: update.payload,
            callback: update.callback,

            next: null}; newLastBaseUpdate = newLastBaseUpdate.next = clone; }// Calculate the new state according to update
        newState = getStateFromUpdate(
          workInProgress,
          queue,
          update,
          newState,
          props,
          instance,
        );
        const callback = update.callback;
        // The callback function pushes into the Effects array
        if(callback ! = =null) {
          workInProgress.flags |= Callback;
          const effects = queue.effects;
          if (effects === null) {
            queue.effects = [update];
          } else{ effects.push(update); }}}// Next update
      update = update.next;
      if (update === null) {
        pendingQueue = queue.shared.pending;
        if (pendingQueue === null) {
          break;
        } else {
          // call setState again in the setState function to enter the judgment
          const lastPendingUpdate = pendingQueue;
          const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
          lastPendingUpdate.next = null;
          update = firstPendingUpdate;
          queue.lastBaseUpdate = lastPendingUpdate;
          queue.shared.pending = null; }}}while (true);

    if (newLastBaseUpdate === null) {
      newBaseState = newState;
    }

    queue.baseState = ((newBaseState: any): State);
    queue.firstBaseUpdate = newFirstBaseUpdate;
    queue.lastBaseUpdate = newLastBaseUpdate;

    markSkippedUpdateLanes(newLanes);
    workInProgress.lanes = newLanes;
    workInProgress.memoizedState = newState; // reflect the state value of the page}}Copy the code

The processUpdateQueue method performs updateQueue to calculate the new state value, mainly because it contains the priority and skipped Update. The main logic is to iterate through the Update list to get the new state.

The state value is obtained using the getStateFromUpdate method.

getStateFromUpdate

State is handled within the getStateFromUpdate function based on the Update type, focusing on the UpdateState type initiated by setState.

function getStateFromUpdate<State> (workInProgress: Fiber, queue: UpdateQueue
       
        , update: Update
        
         , prevState: State, nextProps: any, instance: any,
        
       ) :any {
  switch (update.tag) {
    // omit code...
    / / tag for UpdateState
    case UpdateState: {
      const payload = update.payload;
      let partialState;
      if (typeof payload === 'function') {
        // When the first argument is a function
        partialState = payload.call(instance, prevState, nextProps);
      } else {
        // The first argument is object
        partialState = payload;
      }
      if (partialState === null || partialState === undefined) {
        return prevState;
      }
      / / state
      return Object.assign({}, prevState, partialState); },/ / tag for ForceUpdate
    case ForceUpdate: {
      hasForceUpdate = true;
      returnprevState; }}return prevState;
}

Copy the code

SetState and forceUpdate

The setState and forceUpdate methods are methods that inherit from the Component prototype chain and are essentially called at Mount when this.updater is assigned.

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

This. Updater is assigned to constructClassInstance when adoptClassInstance is called in the constructClassInstance method at Mount time.

// adoptClassInstance...
instance.updater = classComponentUpdater;
// Access the fiber node through the instance._reactInternals property
setInstance(instance, workInProgress);
Copy the code

classComponentUpdater

Updater essentially calls the classComponentUpdater method.

const classComponentUpdater = {
  enqueueSetState(inst, payload, callback) {
    // Fetch the fiber node from the _reactInternals property of the instance
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const lane = requestUpdateLane(fiber);

    / / create the update
    const update = createUpdate(eventTime, lane);
    update.payload = payload;
    if(callback ! = =undefined&& callback ! = =null) {
      update.callback = callback;
    }

    / / Update the team
    enqueueUpdate(fiber, update);
    // Initiate update scheduling
    scheduleUpdateOnFiber(fiber, lane, eventTime);
  },
  enqueueForceUpdate(inst, callback) {
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const lane = requestUpdateLane(fiber);

    const update = createUpdate(eventTime, lane);
    update.tag = ForceUpdate;

    if(callback ! = =undefined&& callback ! = =null) { update.callback = callback; } enqueueUpdate(fiber, update); scheduleUpdateOnFiber(fiber, lane, eventTime); }};Copy the code

The main difference between setState and forceUpdate is that the tag type of Update is different. The getStateFromUpdate method returns different results when updateQueue obtains the state from Update.

Their callbacks are executed during subsequent COMMIT phases.

Class component lifecycle execution

The following life cycle does not include the UNSAFE life cycle, which is not recommended for future use, and some extraneous code is omitted.

Render phase

With concurrent mode enabled in the future, the current UNSAFE lifecycle may be executed multiple times due to render interrupts or high priorities, and while this is not an issue for now, it should be avoided.

constructor

Constructor actually passes context as a second argument when executing, which is not present on the document.

function constructClassInstance(
// ...
) :any {
  // Create an instance
  const instance = new ctor(props, context);
}
Copy the code

shouldComponentUpdate

ShouldComponentUpdate is called every time in the updateClassInstance method during Update.

function updateClassInstance(
// ...
) :boolean {
  const shouldUpdate =
  checkHasForceUpdateAfterProcessing() ||
   / / call shouldComponentUpdate
  checkShouldComponentUpdate(
    workInProgress,
    ctor,
    oldProps,
    newProps,
    oldState,
    newState,
    nextContext,
  );
}
Copy the code

getDerivedStateFromProps

GetDerivedStateFromProps will trigger this method before each render, as documented, and it will be called at Mount and Update to get a new state value by executing the function, which will be merged to the current state, It also maintains the base Estate value for updateQueue in some cases.

// ...
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
  applyDerivedStateFromProps(
    workInProgress,
    ctor,
    getDerivedStateFromProps,
    newProps,
  );
  / / assigned to
  instance.state = workInProgress.memoizedState;
}
Copy the code

componentDidMount

Although componentDidMount is not executed during render, it is flagged during Render and only executed during commit.

function mountClassInstance(
// ...
) :void {
  // Flags increment Update with componentDidMount
  if (typeof instance.componentDidMount === 'function') { workInProgress.flags |= Update; }}Copy the code

ComponentDidMount and getSnapshotBeforeUpdate

As with componentDidMount, flags is required for render, but it checks whether props and state are changed.

function updateClassInstance(
// ...
) :boolean {
  if (typeof instance.componentDidUpdate === 'function') {
    if( unresolvedOldProps ! == current.memoizedProps || oldState ! == current.memoizedState ) { workInProgress.flags |= Update; }}if (typeof instance.getSnapshotBeforeUpdate === 'function') {
    if (
      unresolvedOldProps !== current.memoizedProps ||
      oldState !== current.memoizedState
    ) {
      workInProgress.flags |= Snapshot;
    }
  }
}
Copy the code

The commit phase

The COMMIT phase is divided into three phases

  • BeforeMutation phase
  • Mutation stages
  • Layout stage

BeforeMutation phase

BeforeMutation phase before DOM manipulation.

getSnapshotBeforeUpdate

Check whether current is Update and execute the getSnapshotBeforeUpdate method. As described in the document, this life cycle can be used as the scrollbar position because the DOM value is still old.

function commitBeforeMutationLifeCycles(
  current: Fiber | null,
  finishedWork: Fiber,
) :void {
  switch (finishedWork.tag) {
    // ...
    case ClassComponent: {
      // 判断flags有Snapshot,在render阶段会打上flags
      if (finishedWork.flags & Snapshot) {
        // Check whether the current is Mount or Update using current
        if(current ! = =null) {
          const prevProps = current.memoizedProps;
          const prevState = current.memoizedState;
          const instance = finishedWork.stateNode;
          
          const snapshot = instance.getSnapshotBeforeUpdate(
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );
          // The return value is stored in an internal variableinstance.__reactInternalSnapshotBeforeUpdate = snapshot; }}return;
    }
    // ...}}Copy the code

Mutation stages

The Mution phase is manipulating the DOM.

componentWillUnMount

When Fiber node contains flags Deletion, finally get into commitUnmount method, by calling componentWillUnMount safelyCallComponentWillUnmount method.

function commitUnmount(finishedRoot: FiberRoot, current: Fiber, renderPriorityLevel: ReactPriorityLevel,) :void {
  switch (current.tag) {
    // ...
    case ClassComponent: {
      safelyDetachRef(current);
      const instance = current.stateNode;
      if (typeof instance.componentWillUnmount === 'function') {
        safelyCallComponentWillUnmount(current, instance);
      }
      return;
    }
    // ...}}Copy the code

Layout stage

The Layou phase is after DOM manipulation.

ComponentDidMount and componentDidMount

ComponentDidMount and componentDidMount are executed in the same function. Current === null determines which function to execute.

function commitLifeCycles(
  finishedRoot: FiberRoot,
  current: Fiber | null,
  finishedWork: Fiber,
  committedLanes: Lanes,
) :void {
  switch (finishedWork.tag) {
    case ClassComponent: {
      const instance = finishedWork.stateNode;
      if (finishedWork.flags & Update) {
        / / the mount
        if (current === null) {
          / / componentDidMount execution
          instance.componentDidMount();
        } else {
          // Update the props and state from the current Fiber node
          const prevProps =
            finishedWork.elementType === finishedWork.type
              ? current.memoizedProps
              : resolveDefaultProps(finishedWork.type, current.memoizedProps);
          constprevState = current.memoizedState; instance.componentDidUpdate( prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate, ); }}// Effects are performed on updateQueue, where setState and forceUpdate callback are stored
      const updateQueue: UpdateQueue<
        *,
      > | null = (finishedWork.updateQueue: any);
      if(updateQueue ! = =null) {
        commitUpdateQueue(finishedWork, updateQueue, instance);
      }
      return; }}}Copy the code

conclusion

More than simple simple analyses the type of Fiber components from source node to create, component class instance creation, update, update the scheduling, the implementation of life cycle, contrast Hooks back, I think some kind of components is quite convenient, though most functions components can be implemented, but still take some time to realize the corresponding function with Hooks.

Today looked at the source code class article written by others, I can not help but feel that I write is probably to see their own, the basic calculation of a note.