Best JavaScript code snippet using playwright-internal
ReactFiberScheduler.new.js
Source:ReactFiberScheduler.new.js  
...924        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);925      }926      stopWorkTimer(workInProgress);927      resetCurrentDebugFiberInDEV();928      resetChildExpirationTime(workInProgress);929      if (next !== null) {930        // Completing this fiber spawned new work. Work on that next.931        return next;932      }933      if (934        returnFiber !== null &&935        // Do not append effects to parents if a sibling failed to complete936        (returnFiber.effectTag & Incomplete) === NoEffect937      ) {938        // Append all the effects of the subtree and this fiber onto the effect939        // list of the parent. The completion order of the children affects the940        // side-effect order.941        if (returnFiber.firstEffect === null) {942          returnFiber.firstEffect = workInProgress.firstEffect;943        }944        if (workInProgress.lastEffect !== null) {945          if (returnFiber.lastEffect !== null) {946            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;947          }948          returnFiber.lastEffect = workInProgress.lastEffect;949        }950        // If this fiber had side-effects, we append it AFTER the children's951        // side-effects. We can perform certain side-effects earlier if needed,952        // by doing multiple passes over the effect list. We don't want to953        // schedule our own side-effect on our own list because if end up954        // reusing children we'll schedule this effect onto itself since we're955        // at the end.956        const effectTag = workInProgress.effectTag;957        // Skip both NoWork and PerformedWork tags when creating the effect958        // list. PerformedWork effect is read by React DevTools but shouldn't be959        // committed.960        if (effectTag > PerformedWork) {961          if (returnFiber.lastEffect !== null) {962            returnFiber.lastEffect.nextEffect = workInProgress;963          } else {964            returnFiber.firstEffect = workInProgress;965          }966          returnFiber.lastEffect = workInProgress;967        }968      }969    } else {970      // This fiber did not complete because something threw. Pop values off971      // the stack without entering the complete phase. If this is a boundary,972      // capture values if possible.973      const next = unwindWork(workInProgress, renderExpirationTime);974      // Because this fiber did not complete, don't reset its expiration time.975      if (976        enableProfilerTimer &&977        (workInProgress.mode & ProfileMode) !== NoContext978      ) {979        // Record the render duration for the fiber that errored.980        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);981        // Include the time spent working on failed children before continuing.982        let actualDuration = workInProgress.actualDuration;983        let child = workInProgress.child;984        while (child !== null) {985          actualDuration += child.actualDuration;986          child = child.sibling;987        }988        workInProgress.actualDuration = actualDuration;989      }990      if (next !== null) {991        // If completing this work spawned new work, do that next. We'll come992        // back here again.993        // Since we're restarting, remove anything that is not a host effect994        // from the effect tag.995        // TODO: The name stopFailedWorkTimer is misleading because Suspense996        // also captures and restarts.997        stopFailedWorkTimer(workInProgress);998        next.effectTag &= HostEffectMask;999        return next;1000      }1001      stopWorkTimer(workInProgress);1002      if (returnFiber !== null) {1003        // Mark the parent fiber as incomplete and clear its effect list.1004        returnFiber.firstEffect = returnFiber.lastEffect = null;1005        returnFiber.effectTag |= Incomplete;1006      }1007    }1008    const siblingFiber = workInProgress.sibling;1009    if (siblingFiber !== null) {1010      // If there is more work to do in this returnFiber, do that next.1011      return siblingFiber;1012    }1013    // Otherwise, return to the parent1014    workInProgress = returnFiber;1015  } while (workInProgress !== null);1016  // We've reached the root.1017  if (workInProgressRootExitStatus === RootIncomplete) {1018    workInProgressRootExitStatus = RootCompleted;1019  }1020  return null;1021}1022function resetChildExpirationTime(completedWork: Fiber) {1023  if (1024    renderExpirationTime !== Never &&1025    completedWork.childExpirationTime === Never1026  ) {1027    // The children of this component are hidden. Don't bubble their1028    // expiration times.1029    return;1030  }1031  let newChildExpirationTime = NoWork;1032  // Bubble up the earliest expiration time.1033  if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1034    // In profiling mode, resetChildExpirationTime is also used to reset1035    // profiler durations.1036    let actualDuration = completedWork.actualDuration;...ReactFiberCompleteWork.new.js
Source:ReactFiberCompleteWork.new.js  
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */9import type {Fiber} from './ReactInternalTypes';10import type {Lanes, Lane} from './ReactFiberLane';11import type {12  ReactFundamentalComponentInstance,13  ReactScopeInstance,14} from 'shared/ReactTypes';15import type {FiberRoot} from './ReactInternalTypes';16import type {17  Instance,18  Type,19  Props,20  Container,21  ChildSet,22} from './ReactFiberHostConfig';23import type {24  SuspenseState,25  SuspenseListRenderState,26} from './ReactFiberSuspenseComponent.new';27import type {SuspenseContext} from './ReactFiberSuspenseContext.new';28import type {OffscreenState} from './ReactFiberOffscreenComponent';29import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new';30import {now} from './SchedulerWithReactIntegration.new';31import {32  IndeterminateComponent,33  FunctionComponent,34  ClassComponent,35  HostRoot,36  HostComponent,37  HostText,38  HostPortal,39  ContextProvider,40  ContextConsumer,41  ForwardRef,42  Fragment,43  Mode,44  Profiler,45  SuspenseComponent,46  SuspenseListComponent,47  MemoComponent,48  SimpleMemoComponent,49  LazyComponent,50  IncompleteClassComponent,51  FundamentalComponent,52  ScopeComponent,53  Block,54  OffscreenComponent,55  LegacyHiddenComponent,56} from './ReactWorkTags';57import {58  NoMode,59  BlockingMode,60  ConcurrentMode,61  ProfileMode,62} from './ReactTypeOfMode';63import {64  Ref,65  Update,66  Callback,67  Passive,68  Deletion,69  NoFlags,70  DidCapture,71  Snapshot,72  MutationMask,73  LayoutMask,74  PassiveMask,75  StaticMask,76  PerformedWork,77} from './ReactFiberFlags';78import invariant from 'shared/invariant';79import {80  createInstance,81  createTextInstance,82  appendInitialChild,83  finalizeInitialChildren,84  prepareUpdate,85  supportsMutation,86  supportsPersistence,87  cloneInstance,88  cloneHiddenInstance,89  cloneHiddenTextInstance,90  createContainerChildSet,91  appendChildToContainerChildSet,92  finalizeContainerChildren,93  getFundamentalComponentInstance,94  mountFundamentalComponent,95  cloneFundamentalInstance,96  shouldUpdateFundamentalComponent,97  preparePortalMount,98  prepareScopeUpdate,99} from './ReactFiberHostConfig';100import {101  getRootHostContainer,102  popHostContext,103  getHostContext,104  popHostContainer,105} from './ReactFiberHostContext.new';106import {107  suspenseStackCursor,108  InvisibleParentSuspenseContext,109  hasSuspenseContext,110  popSuspenseContext,111  pushSuspenseContext,112  setShallowSuspenseContext,113  ForceSuspenseFallback,114  setDefaultShallowSuspenseContext,115} from './ReactFiberSuspenseContext.new';116import {findFirstSuspended} from './ReactFiberSuspenseComponent.new';117import {118  isContextProvider as isLegacyContextProvider,119  popContext as popLegacyContext,120  popTopLevelContextObject as popTopLevelLegacyContextObject,121} from './ReactFiberContext.new';122import {popProvider} from './ReactFiberNewContext.new';123import {124  prepareToHydrateHostInstance,125  prepareToHydrateHostTextInstance,126  prepareToHydrateHostSuspenseInstance,127  popHydrationState,128  resetHydrationState,129  getIsHydrating,130} from './ReactFiberHydrationContext.new';131import {132  enableSchedulerTracing,133  enableSuspenseCallback,134  enableSuspenseServerRenderer,135  enableFundamentalAPI,136  enableScopeAPI,137  enableBlocksAPI,138  enableProfilerTimer,139} from 'shared/ReactFeatureFlags';140import {141  markSpawnedWork,142  renderDidSuspend,143  renderDidSuspendDelayIfPossible,144  renderHasNotSuspendedYet,145  popRenderLanes,146  getRenderTargetTime,147  subtreeRenderLanes,148} from './ReactFiberWorkLoop.new';149import {createFundamentalStateInstance} from './ReactFiberFundamental.new';150import {151  OffscreenLane,152  SomeRetryLane,153  NoLanes,154  includesSomeLane,155  mergeLanes,156} from './ReactFiberLane';157import {resetChildFibers} from './ReactChildFiber.new';158import {createScopeInstance} from './ReactFiberScope.new';159import {transferActualDuration} from './ReactProfilerTimer.new';160function markUpdate(workInProgress: Fiber) {161  // Tag the fiber with an update effect. This turns a Placement into162  // a PlacementAndUpdate.163  workInProgress.flags |= Update;164}165function markRef(workInProgress: Fiber) {166  workInProgress.flags |= Ref;167}168function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) {169  const didBailout = current !== null && current.child === completedWork.child;170  if (didBailout) {171    return true;172  }173  let child = completedWork.child;174  while (child !== null) {175    if ((child.flags & MutationMask) !== NoFlags) {176      return false;177    }178    if ((child.subtreeFlags & MutationMask) !== NoFlags) {179      return false;180    }181    child = child.sibling;182  }183  return true;184}185let appendAllChildren;186let updateHostContainer;187let updateHostComponent;188let updateHostText;189if (supportsMutation) {190  // Mutation mode191  appendAllChildren = function(192    parent: Instance,193    workInProgress: Fiber,194    needsVisibilityToggle: boolean,195    isHidden: boolean,196  ) {197    // We only have the top Fiber that was created but we need recurse down its198    // children to find all the terminal nodes.199    let node = workInProgress.child;200    while (node !== null) {201      if (node.tag === HostComponent || node.tag === HostText) {202        appendInitialChild(parent, node.stateNode);203      } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {204        appendInitialChild(parent, node.stateNode.instance);205      } else if (node.tag === HostPortal) {206        // If we have a portal child, then we don't want to traverse207        // down its children. Instead, we'll get insertions from each child in208        // the portal directly.209      } else if (node.child !== null) {210        node.child.return = node;211        node = node.child;212        continue;213      }214      if (node === workInProgress) {215        return;216      }217      while (node.sibling === null) {218        if (node.return === null || node.return === workInProgress) {219          return;220        }221        node = node.return;222      }223      node.sibling.return = node.return;224      node = node.sibling;225    }226  };227  updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {228    // Noop229  };230  updateHostComponent = function(231    current: Fiber,232    workInProgress: Fiber,233    type: Type,234    newProps: Props,235    rootContainerInstance: Container,236  ) {237    // If we have an alternate, that means this is an update and we need to238    // schedule a side-effect to do the updates.239    const oldProps = current.memoizedProps;240    if (oldProps === newProps) {241      // In mutation mode, this is sufficient for a bailout because242      // we won't touch this node even if children changed.243      return;244    }245    // If we get updated because one of our children updated, we don't246    // have newProps so we'll have to reuse them.247    // TODO: Split the update API as separate for the props vs. children.248    // Even better would be if children weren't special cased at all tho.249    const instance: Instance = workInProgress.stateNode;250    const currentHostContext = getHostContext();251    // TODO: Experiencing an error where oldProps is null. Suggests a host252    // component is hitting the resume path. Figure out why. Possibly253    // related to `hidden`.254    const updatePayload = prepareUpdate(255      instance,256      type,257      oldProps,258      newProps,259      rootContainerInstance,260      currentHostContext,261    );262    // TODO: Type this specific to this type of component.263    workInProgress.updateQueue = (updatePayload: any);264    // If the update payload indicates that there is a change or if there265    // is a new ref we mark this as an update. All the work is done in commitWork.266    if (updatePayload) {267      markUpdate(workInProgress);268    }269  };270  updateHostText = function(271    current: Fiber,272    workInProgress: Fiber,273    oldText: string,274    newText: string,275  ) {276    // If the text differs, mark it as an update. All the work in done in commitWork.277    if (oldText !== newText) {278      markUpdate(workInProgress);279    }280  };281} else if (supportsPersistence) {282  // Persistent host tree mode283  appendAllChildren = function(284    parent: Instance,285    workInProgress: Fiber,286    needsVisibilityToggle: boolean,287    isHidden: boolean,288  ) {289    // We only have the top Fiber that was created but we need recurse down its290    // children to find all the terminal nodes.291    let node = workInProgress.child;292    while (node !== null) {293      // eslint-disable-next-line no-labels294      branches: if (node.tag === HostComponent) {295        let instance = node.stateNode;296        if (needsVisibilityToggle && isHidden) {297          // This child is inside a timed out tree. Hide it.298          const props = node.memoizedProps;299          const type = node.type;300          instance = cloneHiddenInstance(instance, type, props, node);301        }302        appendInitialChild(parent, instance);303      } else if (node.tag === HostText) {304        let instance = node.stateNode;305        if (needsVisibilityToggle && isHidden) {306          // This child is inside a timed out tree. Hide it.307          const text = node.memoizedProps;308          instance = cloneHiddenTextInstance(instance, text, node);309        }310        appendInitialChild(parent, instance);311      } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {312        let instance = node.stateNode.instance;313        if (needsVisibilityToggle && isHidden) {314          // This child is inside a timed out tree. Hide it.315          const props = node.memoizedProps;316          const type = node.type;317          instance = cloneHiddenInstance(instance, type, props, node);318        }319        appendInitialChild(parent, instance);320      } else if (node.tag === HostPortal) {321        // If we have a portal child, then we don't want to traverse322        // down its children. Instead, we'll get insertions from each child in323        // the portal directly.324      } else if (node.tag === SuspenseComponent) {325        if ((node.flags & Update) !== NoFlags) {326          // Need to toggle the visibility of the primary children.327          const newIsHidden = node.memoizedState !== null;328          if (newIsHidden) {329            const primaryChildParent = node.child;330            if (primaryChildParent !== null) {331              if (primaryChildParent.child !== null) {332                primaryChildParent.child.return = primaryChildParent;333                appendAllChildren(334                  parent,335                  primaryChildParent,336                  true,337                  newIsHidden,338                );339              }340              const fallbackChildParent = primaryChildParent.sibling;341              if (fallbackChildParent !== null) {342                fallbackChildParent.return = node;343                node = fallbackChildParent;344                continue;345              }346            }347          }348        }349        if (node.child !== null) {350          // Continue traversing like normal351          node.child.return = node;352          node = node.child;353          continue;354        }355      } else if (node.child !== null) {356        node.child.return = node;357        node = node.child;358        continue;359      }360      // $FlowFixMe This is correct but Flow is confused by the labeled break.361      node = (node: Fiber);362      if (node === workInProgress) {363        return;364      }365      while (node.sibling === null) {366        if (node.return === null || node.return === workInProgress) {367          return;368        }369        node = node.return;370      }371      node.sibling.return = node.return;372      node = node.sibling;373    }374  };375  // An unfortunate fork of appendAllChildren because we have two different parent types.376  const appendAllChildrenToContainer = function(377    containerChildSet: ChildSet,378    workInProgress: Fiber,379    needsVisibilityToggle: boolean,380    isHidden: boolean,381  ) {382    // We only have the top Fiber that was created but we need recurse down its383    // children to find all the terminal nodes.384    let node = workInProgress.child;385    while (node !== null) {386      // eslint-disable-next-line no-labels387      branches: if (node.tag === HostComponent) {388        let instance = node.stateNode;389        if (needsVisibilityToggle && isHidden) {390          // This child is inside a timed out tree. Hide it.391          const props = node.memoizedProps;392          const type = node.type;393          instance = cloneHiddenInstance(instance, type, props, node);394        }395        appendChildToContainerChildSet(containerChildSet, instance);396      } else if (node.tag === HostText) {397        let instance = node.stateNode;398        if (needsVisibilityToggle && isHidden) {399          // This child is inside a timed out tree. Hide it.400          const text = node.memoizedProps;401          instance = cloneHiddenTextInstance(instance, text, node);402        }403        appendChildToContainerChildSet(containerChildSet, instance);404      } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {405        let instance = node.stateNode.instance;406        if (needsVisibilityToggle && isHidden) {407          // This child is inside a timed out tree. Hide it.408          const props = node.memoizedProps;409          const type = node.type;410          instance = cloneHiddenInstance(instance, type, props, node);411        }412        appendChildToContainerChildSet(containerChildSet, instance);413      } else if (node.tag === HostPortal) {414        // If we have a portal child, then we don't want to traverse415        // down its children. Instead, we'll get insertions from each child in416        // the portal directly.417      } else if (node.tag === SuspenseComponent) {418        if ((node.flags & Update) !== NoFlags) {419          // Need to toggle the visibility of the primary children.420          const newIsHidden = node.memoizedState !== null;421          if (newIsHidden) {422            const primaryChildParent = node.child;423            if (primaryChildParent !== null) {424              if (primaryChildParent.child !== null) {425                primaryChildParent.child.return = primaryChildParent;426                appendAllChildrenToContainer(427                  containerChildSet,428                  primaryChildParent,429                  true,430                  newIsHidden,431                );432              }433              const fallbackChildParent = primaryChildParent.sibling;434              if (fallbackChildParent !== null) {435                fallbackChildParent.return = node;436                node = fallbackChildParent;437                continue;438              }439            }440          }441        }442        if (node.child !== null) {443          // Continue traversing like normal444          node.child.return = node;445          node = node.child;446          continue;447        }448      } else if (node.child !== null) {449        node.child.return = node;450        node = node.child;451        continue;452      }453      // $FlowFixMe This is correct but Flow is confused by the labeled break.454      node = (node: Fiber);455      if (node === workInProgress) {456        return;457      }458      while (node.sibling === null) {459        if (node.return === null || node.return === workInProgress) {460          return;461        }462        node = node.return;463      }464      node.sibling.return = node.return;465      node = node.sibling;466    }467  };468  updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {469    const portalOrRoot: {470      containerInfo: Container,471      pendingChildren: ChildSet,472      ...473    } = workInProgress.stateNode;474    const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);475    if (childrenUnchanged) {476      // No changes, just reuse the existing instance.477    } else {478      const container = portalOrRoot.containerInfo;479      const newChildSet = createContainerChildSet(container);480      // If children might have changed, we have to add them all to the set.481      appendAllChildrenToContainer(newChildSet, workInProgress, false, false);482      portalOrRoot.pendingChildren = newChildSet;483      // Schedule an update on the container to swap out the container.484      markUpdate(workInProgress);485      finalizeContainerChildren(container, newChildSet);486    }487  };488  updateHostComponent = function(489    current: Fiber,490    workInProgress: Fiber,491    type: Type,492    newProps: Props,493    rootContainerInstance: Container,494  ) {495    const currentInstance = current.stateNode;496    const oldProps = current.memoizedProps;497    // If there are no effects associated with this node, then none of our children had any updates.498    // This guarantees that we can reuse all of them.499    const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);500    if (childrenUnchanged && oldProps === newProps) {501      // No changes, just reuse the existing instance.502      // Note that this might release a previous clone.503      workInProgress.stateNode = currentInstance;504      return;505    }506    const recyclableInstance: Instance = workInProgress.stateNode;507    const currentHostContext = getHostContext();508    let updatePayload = null;509    if (oldProps !== newProps) {510      updatePayload = prepareUpdate(511        recyclableInstance,512        type,513        oldProps,514        newProps,515        rootContainerInstance,516        currentHostContext,517      );518    }519    if (childrenUnchanged && updatePayload === null) {520      // No changes, just reuse the existing instance.521      // Note that this might release a previous clone.522      workInProgress.stateNode = currentInstance;523      return;524    }525    const newInstance = cloneInstance(526      currentInstance,527      updatePayload,528      type,529      oldProps,530      newProps,531      workInProgress,532      childrenUnchanged,533      recyclableInstance,534    );535    if (536      finalizeInitialChildren(537        newInstance,538        type,539        newProps,540        rootContainerInstance,541        currentHostContext,542      )543    ) {544      markUpdate(workInProgress);545    }546    workInProgress.stateNode = newInstance;547    if (childrenUnchanged) {548      // If there are no other effects in this tree, we need to flag this node as having one.549      // Even though we're not going to use it for anything.550      // Otherwise parents won't know that there are new children to propagate upwards.551      markUpdate(workInProgress);552    } else {553      // If children might have changed, we have to add them all to the set.554      appendAllChildren(newInstance, workInProgress, false, false);555    }556  };557  updateHostText = function(558    current: Fiber,559    workInProgress: Fiber,560    oldText: string,561    newText: string,562  ) {563    if (oldText !== newText) {564      // If the text content differs, we'll create a new text instance for it.565      const rootContainerInstance = getRootHostContainer();566      const currentHostContext = getHostContext();567      workInProgress.stateNode = createTextInstance(568        newText,569        rootContainerInstance,570        currentHostContext,571        workInProgress,572      );573      // We'll have to mark it as having an effect, even though we won't use the effect for anything.574      // This lets the parents know that at least one of their children has changed.575      markUpdate(workInProgress);576    } else {577      workInProgress.stateNode = current.stateNode;578    }579  };580} else {581  // No host operations582  updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {583    // Noop584  };585  updateHostComponent = function(586    current: Fiber,587    workInProgress: Fiber,588    type: Type,589    newProps: Props,590    rootContainerInstance: Container,591  ) {592    // Noop593  };594  updateHostText = function(595    current: Fiber,596    workInProgress: Fiber,597    oldText: string,598    newText: string,599  ) {600    // Noop601  };602}603function cutOffTailIfNeeded(604  renderState: SuspenseListRenderState,605  hasRenderedATailFallback: boolean,606) {607  if (getIsHydrating()) {608    // If we're hydrating, we should consume as many items as we can609    // so we don't leave any behind.610    return;611  }612  switch (renderState.tailMode) {613    case 'hidden': {614      // Any insertions at the end of the tail list after this point615      // should be invisible. If there are already mounted boundaries616      // anything before them are not considered for collapsing.617      // Therefore we need to go through the whole tail to find if618      // there are any.619      let tailNode = renderState.tail;620      let lastTailNode = null;621      while (tailNode !== null) {622        if (tailNode.alternate !== null) {623          lastTailNode = tailNode;624        }625        tailNode = tailNode.sibling;626      }627      // Next we're simply going to delete all insertions after the628      // last rendered item.629      if (lastTailNode === null) {630        // All remaining items in the tail are insertions.631        renderState.tail = null;632      } else {633        // Detach the insertion after the last node that was already634        // inserted.635        lastTailNode.sibling = null;636      }637      break;638    }639    case 'collapsed': {640      // Any insertions at the end of the tail list after this point641      // should be invisible. If there are already mounted boundaries642      // anything before them are not considered for collapsing.643      // Therefore we need to go through the whole tail to find if644      // there are any.645      let tailNode = renderState.tail;646      let lastTailNode = null;647      while (tailNode !== null) {648        if (tailNode.alternate !== null) {649          lastTailNode = tailNode;650        }651        tailNode = tailNode.sibling;652      }653      // Next we're simply going to delete all insertions after the654      // last rendered item.655      if (lastTailNode === null) {656        // All remaining items in the tail are insertions.657        if (!hasRenderedATailFallback && renderState.tail !== null) {658          // We suspended during the head. We want to show at least one659          // row at the tail. So we'll keep on and cut off the rest.660          renderState.tail.sibling = null;661        } else {662          renderState.tail = null;663        }664      } else {665        // Detach the insertion after the last node that was already666        // inserted.667        lastTailNode.sibling = null;668      }669      break;670    }671  }672}673function bubbleProperties(completedWork: Fiber) {674  const didBailout =675    completedWork.alternate !== null &&676    completedWork.alternate.child === completedWork.child;677  let newChildLanes = NoLanes;678  let subtreeFlags = NoFlags;679  if (!didBailout) {680    // Bubble up the earliest expiration time.681    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {682      // In profiling mode, resetChildExpirationTime is also used to reset683      // profiler durations.684      let actualDuration = completedWork.actualDuration;685      let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);686      let child = completedWork.child;687      while (child !== null) {688        newChildLanes = mergeLanes(689          newChildLanes,690          mergeLanes(child.lanes, child.childLanes),691        );692        subtreeFlags |= child.subtreeFlags;693        subtreeFlags |= child.flags;694        // When a fiber is cloned, its actualDuration is reset to 0. This value will695        // only be updated if work is done on the fiber (i.e. it doesn't bailout).696        // When work is done, it should bubble to the parent's actualDuration. If697        // the fiber has not been cloned though, (meaning no work was done), then698        // this value will reflect the amount of time spent working on a previous699        // render. In that case it should not bubble. We determine whether it was700        // cloned by comparing the child pointer.701        actualDuration += child.actualDuration;702        treeBaseDuration += child.treeBaseDuration;703        child = child.sibling;704      }705      completedWork.actualDuration = actualDuration;706      completedWork.treeBaseDuration = treeBaseDuration;707    } else {708      let child = completedWork.child;709      while (child !== null) {710        newChildLanes = mergeLanes(711          newChildLanes,712          mergeLanes(child.lanes, child.childLanes),713        );714        subtreeFlags |= child.subtreeFlags;715        subtreeFlags |= child.flags;716        child = child.sibling;717      }718    }719    completedWork.subtreeFlags |= subtreeFlags;720  } else {721    // Bubble up the earliest expiration time.722    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {723      // In profiling mode, resetChildExpirationTime is also used to reset724      // profiler durations.725      let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);726      let child = completedWork.child;727      while (child !== null) {728        newChildLanes = mergeLanes(729          newChildLanes,730          mergeLanes(child.lanes, child.childLanes),731        );732        // "Static" flags share the lifetime of the fiber/hook they belong to,733        // so we should bubble those up even during a bailout. All the other734        // flags have a lifetime only of a single render + commit, so we should735        // ignore them.736        subtreeFlags |= child.subtreeFlags & StaticMask;737        subtreeFlags |= child.flags & StaticMask;738        treeBaseDuration += child.treeBaseDuration;739        child = child.sibling;740      }741      completedWork.treeBaseDuration = treeBaseDuration;742    } else {743      let child = completedWork.child;744      while (child !== null) {745        newChildLanes = mergeLanes(746          newChildLanes,747          mergeLanes(child.lanes, child.childLanes),748        );749        // "Static" flags share the lifetime of the fiber/hook they belong to,750        // so we should bubble those up even during a bailout. All the other751        // flags have a lifetime only of a single render + commit, so we should752        // ignore them.753        subtreeFlags |= child.subtreeFlags & StaticMask;754        subtreeFlags |= child.flags & StaticMask;755        child = child.sibling;756      }757    }758    completedWork.subtreeFlags |= subtreeFlags;759  }760  completedWork.childLanes = newChildLanes;761  return didBailout;762}763function completeWork(764  current: Fiber | null,765  workInProgress: Fiber,766  renderLanes: Lanes,767): Fiber | null {768  const newProps = workInProgress.pendingProps;769  switch (workInProgress.tag) {770    case IndeterminateComponent:771    case LazyComponent:772    case SimpleMemoComponent:773    case FunctionComponent:774    case ForwardRef:775    case Fragment:776    case Mode:777    case ContextConsumer:778    case MemoComponent:779      bubbleProperties(workInProgress);780      return null;781    case ClassComponent: {782      const Component = workInProgress.type;783      if (isLegacyContextProvider(Component)) {784        popLegacyContext(workInProgress);785      }786      bubbleProperties(workInProgress);787      return null;788    }789    case HostRoot: {790      popHostContainer(workInProgress);791      popTopLevelLegacyContextObject(workInProgress);792      resetMutableSourceWorkInProgressVersions();793      const fiberRoot = (workInProgress.stateNode: FiberRoot);794      if (fiberRoot.pendingContext) {795        fiberRoot.context = fiberRoot.pendingContext;796        fiberRoot.pendingContext = null;797      }798      if (current === null || current.child === null) {799        // If we hydrated, pop so that we can delete any remaining children800        // that weren't hydrated.801        const wasHydrated = popHydrationState(workInProgress);802        if (wasHydrated) {803          // If we hydrated, then we'll need to schedule an update for804          // the commit side-effects on the root.805          markUpdate(workInProgress);806        } else if (!fiberRoot.hydrate) {807          // Schedule an effect to clear this container at the start of the next commit.808          // This handles the case of React rendering into a container with previous children.809          // It's also safe to do for updates too, because current.child would only be null810          // if the previous render was null (so the the container would already be empty).811          workInProgress.flags |= Snapshot;812        }813      }814      updateHostContainer(current, workInProgress);815      bubbleProperties(workInProgress);816      return null;817    }818    case HostComponent: {819      popHostContext(workInProgress);820      const rootContainerInstance = getRootHostContainer();821      const type = workInProgress.type;822      if (current !== null && workInProgress.stateNode != null) {823        updateHostComponent(824          current,825          workInProgress,826          type,827          newProps,828          rootContainerInstance,829        );830        if (current.ref !== workInProgress.ref) {831          markRef(workInProgress);832        }833      } else {834        if (!newProps) {835          invariant(836            workInProgress.stateNode !== null,837            'We must have new props for new mounts. This error is likely ' +838              'caused by a bug in React. Please file an issue.',839          );840          // This can happen when we abort work.841          bubbleProperties(workInProgress);842          return null;843        }844        const currentHostContext = getHostContext();845        // TODO: Move createInstance to beginWork and keep it on a context846        // "stack" as the parent. Then append children as we go in beginWork847        // or completeWork depending on whether we want to add them top->down or848        // bottom->up. Top->down is faster in IE11.849        const wasHydrated = popHydrationState(workInProgress);850        if (wasHydrated) {851          // TODO: Move this and createInstance step into the beginPhase852          // to consolidate.853          if (854            prepareToHydrateHostInstance(855              workInProgress,856              rootContainerInstance,857              currentHostContext,858            )859          ) {860            // If changes to the hydrated node need to be applied at the861            // commit-phase we mark this as such.862            markUpdate(workInProgress);863          }864        } else {865          const instance = createInstance(866            type,867            newProps,868            rootContainerInstance,869            currentHostContext,870            workInProgress,871          );872          appendAllChildren(instance, workInProgress, false, false);873          workInProgress.stateNode = instance;874          // Certain renderers require commit-time effects for initial mount.875          // (eg DOM renderer supports auto-focus for certain elements).876          // Make sure such renderers get scheduled for later work.877          if (878            finalizeInitialChildren(879              instance,880              type,881              newProps,882              rootContainerInstance,883              currentHostContext,884            )885          ) {886            markUpdate(workInProgress);887          }888        }889        if (workInProgress.ref !== null) {890          // If there is a ref on a host node we need to schedule a callback891          markRef(workInProgress);892        }893      }894      bubbleProperties(workInProgress);895      return null;896    }897    case HostText: {898      const newText = newProps;899      if (current && workInProgress.stateNode != null) {900        const oldText = current.memoizedProps;901        // If we have an alternate, that means this is an update and we need902        // to schedule a side-effect to do the updates.903        updateHostText(current, workInProgress, oldText, newText);904      } else {905        if (typeof newText !== 'string') {906          invariant(907            workInProgress.stateNode !== null,908            'We must have new props for new mounts. This error is likely ' +909              'caused by a bug in React. Please file an issue.',910          );911          // This can happen when we abort work.912        }913        const rootContainerInstance = getRootHostContainer();914        const currentHostContext = getHostContext();915        const wasHydrated = popHydrationState(workInProgress);916        if (wasHydrated) {917          if (prepareToHydrateHostTextInstance(workInProgress)) {918            markUpdate(workInProgress);919          }920        } else {921          workInProgress.stateNode = createTextInstance(922            newText,923            rootContainerInstance,924            currentHostContext,925            workInProgress,926          );927        }928      }929      bubbleProperties(workInProgress);930      return null;931    }932    case Profiler: {933      const didBailout = bubbleProperties(workInProgress);934      if (!didBailout) {935        // Use subtreeFlags to determine which commit callbacks should fire.936        // TODO: Move this logic to the commit phase, since we already check if937        // a fiber's subtree contains effects. Refactor the commit phase's938        // depth-first traversal so that we can put work tag-specific logic939        // before or after committing a subtree's effects.940        const OnRenderFlag = Update;941        const OnCommitFlag = Callback;942        const OnPostCommitFlag = Passive;943        const subtreeFlags = workInProgress.subtreeFlags;944        const flags = workInProgress.flags;945        let newFlags = flags;946        // Call onRender any time this fiber or its subtree are worked on.947        if (948          (flags & PerformedWork) !== NoFlags ||949          (subtreeFlags & PerformedWork) !== NoFlags950        ) {951          newFlags |= OnRenderFlag;952        }953        // Call onCommit only if the subtree contains layout work, or if it954        // contains deletions, since those might result in unmount work, which955        // we include in the same measure.956        // TODO: Can optimize by using a static flag to track whether a tree957        // contains layout effects, like we do for passive effects.958        if (959          (flags & (LayoutMask | Deletion)) !== NoFlags ||960          (subtreeFlags & (LayoutMask | Deletion)) !== NoFlags961        ) {962          newFlags |= OnCommitFlag;963        }964        // Call onPostCommit only if the subtree contains passive work.965        // Don't have to check for deletions, because Deletion is already966        // a passive flag.967        if (968          (flags & PassiveMask) !== NoFlags ||969          (subtreeFlags & PassiveMask) !== NoFlags970        ) {971          newFlags |= OnPostCommitFlag;972        }973        workInProgress.flags = newFlags;974      } else {975        // This fiber and its subtree bailed out, so don't fire any callbacks.976      }977      return null;978    }979    case SuspenseComponent: {980      popSuspenseContext(workInProgress);981      const nextState: null | SuspenseState = workInProgress.memoizedState;982      if (enableSuspenseServerRenderer) {983        if (nextState !== null && nextState.dehydrated !== null) {984          if (current === null) {985            const wasHydrated = popHydrationState(workInProgress);986            invariant(987              wasHydrated,988              'A dehydrated suspense component was completed without a hydrated node. ' +989                'This is probably a bug in React.',990            );991            prepareToHydrateHostSuspenseInstance(workInProgress);992            if (enableSchedulerTracing) {993              markSpawnedWork(OffscreenLane);994            }995            bubbleProperties(workInProgress);996            if (enableProfilerTimer) {997              if ((workInProgress.mode & ProfileMode) !== NoMode) {998                const isTimedOutSuspense = nextState !== null;999                if (isTimedOutSuspense) {1000                  // Don't count time spent in a timed out Suspense subtree as part of the base duration.1001                  const primaryChildFragment = workInProgress.child;1002                  if (primaryChildFragment !== null) {1003                    // $FlowFixMe Flow doens't support type casting in combiation with the -= operator1004                    workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);1005                  }1006                }1007              }1008            }1009            return null;1010          } else {1011            // We should never have been in a hydration state if we didn't have a current.1012            // However, in some of those paths, we might have reentered a hydration state1013            // and then we might be inside a hydration state. In that case, we'll need to exit out of it.1014            resetHydrationState();1015            if ((workInProgress.flags & DidCapture) === NoFlags) {1016              // This boundary did not suspend so it's now hydrated and unsuspended.1017              workInProgress.memoizedState = null;1018            }1019            // If nothing suspended, we need to schedule an effect to mark this boundary1020            // as having hydrated so events know that they're free to be invoked.1021            // It's also a signal to replay events and the suspense callback.1022            // If something suspended, schedule an effect to attach retry listeners.1023            // So we might as well always mark this.1024            workInProgress.flags |= Update;1025            bubbleProperties(workInProgress);1026            if (enableProfilerTimer) {1027              if ((workInProgress.mode & ProfileMode) !== NoMode) {1028                const isTimedOutSuspense = nextState !== null;1029                if (isTimedOutSuspense) {1030                  // Don't count time spent in a timed out Suspense subtree as part of the base duration.1031                  const primaryChildFragment = workInProgress.child;1032                  if (primaryChildFragment !== null) {1033                    // $FlowFixMe Flow doens't support type casting in combiation with the -= operator1034                    workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);1035                  }1036                }1037              }1038            }1039            return null;1040          }1041        }1042      }1043      if ((workInProgress.flags & DidCapture) !== NoFlags) {1044        // Something suspended. Re-render with the fallback children.1045        workInProgress.lanes = renderLanes;1046        // Do not reset the effect list.1047        if (1048          enableProfilerTimer &&1049          (workInProgress.mode & ProfileMode) !== NoMode1050        ) {1051          transferActualDuration(workInProgress);1052        }1053        // Don't bubble properties in this case.1054        return workInProgress;1055      }1056      const nextDidTimeout = nextState !== null;1057      let prevDidTimeout = false;1058      if (current === null) {1059        if (workInProgress.memoizedProps.fallback !== undefined) {1060          popHydrationState(workInProgress);1061        }1062      } else {1063        const prevState: null | SuspenseState = current.memoizedState;1064        prevDidTimeout = prevState !== null;1065      }1066      if (nextDidTimeout && !prevDidTimeout) {1067        // If this subtreee is running in blocking mode we can suspend,1068        // otherwise we won't suspend.1069        // TODO: This will still suspend a synchronous tree if anything1070        // in the concurrent tree already suspended during this render.1071        // This is a known bug.1072        if ((workInProgress.mode & BlockingMode) !== NoMode) {1073          // TODO: Move this back to throwException because this is too late1074          // if this is a large tree which is common for initial loads. We1075          // don't know if we should restart a render or not until we get1076          // this marker, and this is too late.1077          // If this render already had a ping or lower pri updates,1078          // and this is the first time we know we're going to suspend we1079          // should be able to immediately restart from within throwException.1080          const hasInvisibleChildContext =1081            current === null &&1082            workInProgress.memoizedProps.unstable_avoidThisFallback !== true;1083          if (1084            hasInvisibleChildContext ||1085            hasSuspenseContext(1086              suspenseStackCursor.current,1087              (InvisibleParentSuspenseContext: SuspenseContext),1088            )1089          ) {1090            // If this was in an invisible tree or a new render, then showing1091            // this boundary is ok.1092            renderDidSuspend();1093          } else {1094            // Otherwise, we're going to have to hide content so we should1095            // suspend for longer if possible.1096            renderDidSuspendDelayIfPossible();1097          }1098        }1099      }1100      if (supportsPersistence) {1101        // TODO: Only schedule updates if not prevDidTimeout.1102        if (nextDidTimeout) {1103          // If this boundary just timed out, schedule an effect to attach a1104          // retry listener to the promise. This flag is also used to hide the1105          // primary children.1106          workInProgress.flags |= Update;1107        }1108      }1109      if (supportsMutation) {1110        // TODO: Only schedule updates if these values are non equal, i.e. it changed.1111        if (nextDidTimeout || prevDidTimeout) {1112          // If this boundary just timed out, schedule an effect to attach a1113          // retry listener to the promise. This flag is also used to hide the1114          // primary children. In mutation mode, we also need the flag to1115          // *unhide* children that were previously hidden, so check if this1116          // is currently timed out, too.1117          workInProgress.flags |= Update;1118        }1119      }1120      if (1121        enableSuspenseCallback &&1122        workInProgress.updateQueue !== null &&1123        workInProgress.memoizedProps.suspenseCallback != null1124      ) {1125        // Always notify the callback1126        workInProgress.flags |= Update;1127      }1128      bubbleProperties(workInProgress);1129      if (enableProfilerTimer) {1130        if ((workInProgress.mode & ProfileMode) !== NoMode) {1131          if (nextDidTimeout) {1132            // Don't count time spent in a timed out Suspense subtree as part of the base duration.1133            const primaryChildFragment = workInProgress.child;1134            if (primaryChildFragment !== null) {1135              // $FlowFixMe Flow doens't support type casting in combiation with the -= operator1136              workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);1137            }1138          }1139        }1140      }1141      return null;1142    }1143    case HostPortal:1144      popHostContainer(workInProgress);1145      updateHostContainer(current, workInProgress);1146      if (current === null) {1147        preparePortalMount(workInProgress.stateNode.containerInfo);1148      }1149      bubbleProperties(workInProgress);1150      return null;1151    case ContextProvider:1152      // Pop provider fiber1153      popProvider(workInProgress);1154      bubbleProperties(workInProgress);1155      return null;1156    case IncompleteClassComponent: {1157      // Same as class component case. I put it down here so that the tags are1158      // sequential to ensure this switch is compiled to a jump table.1159      const Component = workInProgress.type;1160      if (isLegacyContextProvider(Component)) {1161        popLegacyContext(workInProgress);1162      }1163      bubbleProperties(workInProgress);1164      return null;1165    }1166    case SuspenseListComponent: {1167      popSuspenseContext(workInProgress);1168      const renderState: null | SuspenseListRenderState =1169        workInProgress.memoizedState;1170      if (renderState === null) {1171        // We're running in the default, "independent" mode.1172        // We don't do anything in this mode.1173        bubbleProperties(workInProgress);1174        return null;1175      }1176      let didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;1177      const renderedTail = renderState.rendering;1178      if (renderedTail === null) {1179        // We just rendered the head.1180        if (!didSuspendAlready) {1181          // This is the first pass. We need to figure out if anything is still1182          // suspended in the rendered set.1183          // If new content unsuspended, but there's still some content that1184          // didn't. Then we need to do a second pass that forces everything1185          // to keep showing their fallbacks.1186          // We might be suspended if something in this render pass suspended, or1187          // something in the previous committed pass suspended. Otherwise,1188          // there's no chance so we can skip the expensive call to1189          // findFirstSuspended.1190          const cannotBeSuspended =1191            renderHasNotSuspendedYet() &&1192            (current === null || (current.flags & DidCapture) === NoFlags);1193          if (!cannotBeSuspended) {1194            let row = workInProgress.child;1195            while (row !== null) {1196              const suspended = findFirstSuspended(row);1197              if (suspended !== null) {1198                didSuspendAlready = true;1199                workInProgress.flags |= DidCapture;1200                cutOffTailIfNeeded(renderState, false);1201                // If this is a newly suspended tree, it might not get committed as1202                // part of the second pass. In that case nothing will subscribe to1203                // its thennables. Instead, we'll transfer its thennables to the1204                // SuspenseList so that it can retry if they resolve.1205                // There might be multiple of these in the list but since we're1206                // going to wait for all of them anyway, it doesn't really matter1207                // which ones gets to ping. In theory we could get clever and keep1208                // track of how many dependencies remain but it gets tricky because1209                // in the meantime, we can add/remove/change items and dependencies.1210                // We might bail out of the loop before finding any but that1211                // doesn't matter since that means that the other boundaries that1212                // we did find already has their listeners attached.1213                const newThennables = suspended.updateQueue;1214                if (newThennables !== null) {1215                  workInProgress.updateQueue = newThennables;1216                  workInProgress.flags |= Update;1217                }1218                // Rerender the whole list, but this time, we'll force fallbacks1219                // to stay in place.1220                // Reset the child fibers to their original state.1221                workInProgress.subtreeFlags = NoFlags;1222                resetChildFibers(workInProgress, renderLanes);1223                // Set up the Suspense Context to force suspense and immediately1224                // rerender the children.1225                pushSuspenseContext(1226                  workInProgress,1227                  setShallowSuspenseContext(1228                    suspenseStackCursor.current,1229                    ForceSuspenseFallback,1230                  ),1231                );1232                // Don't bubble properties in this case.1233                return workInProgress.child;1234              }1235              row = row.sibling;1236            }1237          }1238          if (renderState.tail !== null && now() > getRenderTargetTime()) {1239            // We have already passed our CPU deadline but we still have rows1240            // left in the tail. We'll just give up further attempts to render1241            // the main content and only render fallbacks.1242            workInProgress.flags |= DidCapture;1243            didSuspendAlready = true;1244            cutOffTailIfNeeded(renderState, false);1245            // Since nothing actually suspended, there will nothing to ping this1246            // to get it started back up to attempt the next item. While in terms1247            // of priority this work has the same priority as this current render,1248            // it's not part of the same transition once the transition has1249            // committed. If it's sync, we still want to yield so that it can be1250            // painted. Conceptually, this is really the same as pinging.1251            // We can use any RetryLane even if it's the one currently rendering1252            // since we're leaving it behind on this node.1253            workInProgress.lanes = SomeRetryLane;1254            if (enableSchedulerTracing) {1255              markSpawnedWork(SomeRetryLane);1256            }1257          }1258        } else {1259          cutOffTailIfNeeded(renderState, false);1260        }1261        // Next we're going to render the tail.1262      } else {1263        // Append the rendered row to the child list.1264        if (!didSuspendAlready) {1265          const suspended = findFirstSuspended(renderedTail);1266          if (suspended !== null) {1267            workInProgress.flags |= DidCapture;1268            didSuspendAlready = true;1269            // Ensure we transfer the update queue to the parent so that it doesn't1270            // get lost if this row ends up dropped during a second pass.1271            const newThennables = suspended.updateQueue;1272            if (newThennables !== null) {1273              workInProgress.updateQueue = newThennables;1274              workInProgress.flags |= Update;1275            }1276            cutOffTailIfNeeded(renderState, true);1277            // This might have been modified.1278            if (1279              renderState.tail === null &&1280              renderState.tailMode === 'hidden' &&1281              !renderedTail.alternate &&1282              !getIsHydrating() // We don't cut it if we're hydrating.1283            ) {1284              // We're done.1285              bubbleProperties(workInProgress);1286              return null;1287            }1288          } else if (1289            // The time it took to render last row is greater than the remaining1290            // time we have to render. So rendering one more row would likely1291            // exceed it.1292            now() * 2 - renderState.renderingStartTime >1293              getRenderTargetTime() &&1294            renderLanes !== OffscreenLane1295          ) {1296            // We have now passed our CPU deadline and we'll just give up further1297            // attempts to render the main content and only render fallbacks.1298            // The assumption is that this is usually faster.1299            workInProgress.flags |= DidCapture;1300            didSuspendAlready = true;1301            cutOffTailIfNeeded(renderState, false);1302            // Since nothing actually suspended, there will nothing to ping this1303            // to get it started back up to attempt the next item. If we can show1304            // them, then they really have the same priority as this render.1305            // So we'll pick it back up the very next render pass once we've had1306            // an opportunity to yield for paint.1307            workInProgress.lanes = SomeRetryLane;1308            if (enableSchedulerTracing) {1309              markSpawnedWork(SomeRetryLane);1310            }1311          }1312        }1313        if (renderState.isBackwards) {1314          // The effect list of the backwards tail will have been added1315          // to the end. This breaks the guarantee that life-cycles fire in1316          // sibling order but that isn't a strong guarantee promised by React.1317          // Especially since these might also just pop in during future commits.1318          // Append to the beginning of the list.1319          renderedTail.sibling = workInProgress.child;1320          workInProgress.child = renderedTail;1321        } else {1322          const previousSibling = renderState.last;1323          if (previousSibling !== null) {1324            previousSibling.sibling = renderedTail;1325          } else {1326            workInProgress.child = renderedTail;1327          }1328          renderState.last = renderedTail;1329        }1330      }1331      if (renderState.tail !== null) {1332        // We still have tail rows to render.1333        // Pop a row.1334        const next = renderState.tail;1335        renderState.rendering = next;1336        renderState.tail = next.sibling;1337        renderState.renderingStartTime = now();1338        next.sibling = null;1339        // Restore the context.1340        // TODO: We can probably just avoid popping it instead and only1341        // setting it the first time we go from not suspended to suspended.1342        let suspenseContext = suspenseStackCursor.current;1343        if (didSuspendAlready) {1344          suspenseContext = setShallowSuspenseContext(1345            suspenseContext,1346            ForceSuspenseFallback,1347          );1348        } else {1349          suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);1350        }1351        pushSuspenseContext(workInProgress, suspenseContext);1352        // Do a pass over the next row.1353        // Don't bubble properties in this case.1354        return next;1355      }1356      bubbleProperties(workInProgress);1357      return null;1358    }1359    case FundamentalComponent: {1360      if (enableFundamentalAPI) {1361        const fundamentalImpl = workInProgress.type.impl;1362        let fundamentalInstance: ReactFundamentalComponentInstance<1363          any,1364          any,1365        > | null = workInProgress.stateNode;1366        if (fundamentalInstance === null) {1367          const getInitialState = fundamentalImpl.getInitialState;1368          let fundamentalState;1369          if (getInitialState !== undefined) {1370            fundamentalState = getInitialState(newProps);1371          }1372          fundamentalInstance = workInProgress.stateNode = createFundamentalStateInstance(1373            workInProgress,1374            newProps,1375            fundamentalImpl,1376            fundamentalState || {},1377          );1378          const instance = ((getFundamentalComponentInstance(1379            fundamentalInstance,1380          ): any): Instance);1381          fundamentalInstance.instance = instance;1382          if (fundamentalImpl.reconcileChildren === false) {1383            bubbleProperties(workInProgress);1384            return null;1385          }1386          appendAllChildren(instance, workInProgress, false, false);1387          mountFundamentalComponent(fundamentalInstance);1388        } else {1389          // We fire update in commit phase1390          const prevProps = fundamentalInstance.props;1391          fundamentalInstance.prevProps = prevProps;1392          fundamentalInstance.props = newProps;1393          fundamentalInstance.currentFiber = workInProgress;1394          if (supportsPersistence) {1395            const instance = cloneFundamentalInstance(fundamentalInstance);1396            fundamentalInstance.instance = instance;1397            appendAllChildren(instance, workInProgress, false, false);1398          }1399          const shouldUpdate = shouldUpdateFundamentalComponent(1400            fundamentalInstance,1401          );1402          if (shouldUpdate) {1403            markUpdate(workInProgress);1404          }1405        }1406        bubbleProperties(workInProgress);1407        return null;1408      }1409      break;1410    }1411    case ScopeComponent: {1412      if (enableScopeAPI) {1413        if (current === null) {1414          const scopeInstance: ReactScopeInstance = createScopeInstance();1415          workInProgress.stateNode = scopeInstance;1416          prepareScopeUpdate(scopeInstance, workInProgress);1417          if (workInProgress.ref !== null) {1418            markRef(workInProgress);1419            markUpdate(workInProgress);1420          }1421        } else {1422          if (workInProgress.ref !== null) {1423            markUpdate(workInProgress);1424          }1425          if (current.ref !== workInProgress.ref) {1426            markRef(workInProgress);1427          }1428        }1429        bubbleProperties(workInProgress);1430        return null;1431      }1432      break;1433    }1434    case Block:1435      if (enableBlocksAPI) {1436        bubbleProperties(workInProgress);1437        return null;1438      }1439      break;1440    case OffscreenComponent:1441    case LegacyHiddenComponent: {1442      popRenderLanes(workInProgress);1443      const nextState: OffscreenState | null = workInProgress.memoizedState;1444      const nextIsHidden = nextState !== null;1445      if (current !== null) {1446        const prevState: OffscreenState | null = current.memoizedState;1447        const prevIsHidden = prevState !== null;1448        if (1449          prevIsHidden !== nextIsHidden &&1450          newProps.mode !== 'unstable-defer-without-hiding'1451        ) {1452          workInProgress.flags |= Update;1453        }1454      }1455      // Don't bubble properties for hidden children.1456      if (1457        !nextIsHidden ||1458        includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) ||1459        (workInProgress.mode & ConcurrentMode) === NoMode1460      ) {1461        bubbleProperties(workInProgress);1462      }1463      return null;1464    }1465  }1466  invariant(1467    false,1468    'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +1469      'React. Please file an issue.',1470    workInProgress.tag,1471  );1472}...ReactFiberCompleteWork.old.js
Source:ReactFiberCompleteWork.old.js  
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */9import type {Fiber} from './ReactInternalTypes';10import type {Lanes, Lane} from './ReactFiberLane.old';11import type {ReactScopeInstance, ReactContext} from 'shared/ReactTypes';12import type {FiberRoot} from './ReactInternalTypes';13import type {14  Instance,15  Type,16  Props,17  Container,18  ChildSet,19} from './ReactFiberHostConfig';20import type {21  SuspenseState,22  SuspenseListRenderState,23} from './ReactFiberSuspenseComponent.old';24import type {SuspenseContext} from './ReactFiberSuspenseContext.old';25import type {OffscreenState} from './ReactFiberOffscreenComponent';26import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old';27import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old';28import {now} from './Scheduler';29import {30  IndeterminateComponent,31  FunctionComponent,32  ClassComponent,33  HostRoot,34  HostComponent,35  HostText,36  HostPortal,37  ContextProvider,38  ContextConsumer,39  ForwardRef,40  Fragment,41  Mode,42  Profiler,43  SuspenseComponent,44  SuspenseListComponent,45  MemoComponent,46  SimpleMemoComponent,47  LazyComponent,48  IncompleteClassComponent,49  ScopeComponent,50  OffscreenComponent,51  LegacyHiddenComponent,52  CacheComponent,53} from './ReactWorkTags';54import {NoMode, ConcurrentMode, ProfileMode} from './ReactTypeOfMode';55import {56  Ref,57  RefStatic,58  Update,59  NoFlags,60  DidCapture,61  Snapshot,62  ChildDeletion,63  StaticMask,64  MutationMask,65} from './ReactFiberFlags';66import invariant from 'shared/invariant';67import {68  createInstance,69  createTextInstance,70  appendInitialChild,71  finalizeInitialChildren,72  prepareUpdate,73  supportsMutation,74  supportsPersistence,75  cloneInstance,76  cloneHiddenInstance,77  cloneHiddenTextInstance,78  createContainerChildSet,79  appendChildToContainerChildSet,80  finalizeContainerChildren,81  preparePortalMount,82  prepareScopeUpdate,83} from './ReactFiberHostConfig';84import {85  getRootHostContainer,86  popHostContext,87  getHostContext,88  popHostContainer,89} from './ReactFiberHostContext.old';90import {91  suspenseStackCursor,92  InvisibleParentSuspenseContext,93  hasSuspenseContext,94  popSuspenseContext,95  pushSuspenseContext,96  setShallowSuspenseContext,97  ForceSuspenseFallback,98  setDefaultShallowSuspenseContext,99} from './ReactFiberSuspenseContext.old';100import {findFirstSuspended} from './ReactFiberSuspenseComponent.old';101import {102  isContextProvider as isLegacyContextProvider,103  popContext as popLegacyContext,104  popTopLevelContextObject as popTopLevelLegacyContextObject,105} from './ReactFiberContext.old';106import {popProvider} from './ReactFiberNewContext.old';107import {108  prepareToHydrateHostInstance,109  prepareToHydrateHostTextInstance,110  prepareToHydrateHostSuspenseInstance,111  popHydrationState,112  resetHydrationState,113  getIsHydrating,114} from './ReactFiberHydrationContext.old';115import {116  enableSuspenseCallback,117  enableSuspenseServerRenderer,118  enableScopeAPI,119  enableProfilerTimer,120  enableCache,121  enableSuspenseLayoutEffectSemantics,122} from 'shared/ReactFeatureFlags';123import {124  renderDidSuspend,125  renderDidSuspendDelayIfPossible,126  renderHasNotSuspendedYet,127  popRenderLanes,128  getRenderTargetTime,129  subtreeRenderLanes,130} from './ReactFiberWorkLoop.old';131import {132  OffscreenLane,133  SomeRetryLane,134  NoLanes,135  includesSomeLane,136  mergeLanes,137} from './ReactFiberLane.old';138import {resetChildFibers} from './ReactChildFiber.old';139import {createScopeInstance} from './ReactFiberScope.old';140import {transferActualDuration} from './ReactProfilerTimer.old';141import {142  popCacheProvider,143  popRootCachePool,144  popCachePool,145} from './ReactFiberCacheComponent.old';146function markUpdate(workInProgress: Fiber) {147  // Tag the fiber with an update effect. This turns a Placement into148  // a PlacementAndUpdate.149  workInProgress.flags |= Update;150}151function markRef(workInProgress: Fiber) {152  workInProgress.flags |= Ref;153  if (enableSuspenseLayoutEffectSemantics) {154    workInProgress.flags |= RefStatic;155  }156}157function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) {158  const didBailout = current !== null && current.child === completedWork.child;159  if (didBailout) {160    return true;161  }162  if ((completedWork.flags & ChildDeletion) !== NoFlags) {163    return false;164  }165  // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties`166  // then we only have to check the `completedWork.subtreeFlags`.167  let child = completedWork.child;168  while (child !== null) {169    if (170      (child.flags & MutationMask) !== NoFlags ||171      (child.subtreeFlags & MutationMask) !== NoFlags172    ) {173      return false;174    }175    child = child.sibling;176  }177  return true;178}179let appendAllChildren;180let updateHostContainer;181let updateHostComponent;182let updateHostText;183if (supportsMutation) {184  // Mutation mode185  appendAllChildren = function(186    parent: Instance,187    workInProgress: Fiber,188    needsVisibilityToggle: boolean,189    isHidden: boolean,190  ) {191    // We only have the top Fiber that was created but we need recurse down its192    // children to find all the terminal nodes.193    let node = workInProgress.child;194    while (node !== null) {195      if (node.tag === HostComponent || node.tag === HostText) {196        appendInitialChild(parent, node.stateNode);197      } else if (node.tag === HostPortal) {198        // If we have a portal child, then we don't want to traverse199        // down its children. Instead, we'll get insertions from each child in200        // the portal directly.201      } else if (node.child !== null) {202        node.child.return = node;203        node = node.child;204        continue;205      }206      if (node === workInProgress) {207        return;208      }209      while (node.sibling === null) {210        if (node.return === null || node.return === workInProgress) {211          return;212        }213        node = node.return;214      }215      node.sibling.return = node.return;216      node = node.sibling;217    }218  };219  updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {220    // Noop221  };222  updateHostComponent = function(223    current: Fiber,224    workInProgress: Fiber,225    type: Type,226    newProps: Props,227    rootContainerInstance: Container,228  ) {229    // If we have an alternate, that means this is an update and we need to230    // schedule a side-effect to do the updates.231    const oldProps = current.memoizedProps;232    if (oldProps === newProps) {233      // In mutation mode, this is sufficient for a bailout because234      // we won't touch this node even if children changed.235      return;236    }237    // If we get updated because one of our children updated, we don't238    // have newProps so we'll have to reuse them.239    // TODO: Split the update API as separate for the props vs. children.240    // Even better would be if children weren't special cased at all tho.241    const instance: Instance = workInProgress.stateNode;242    const currentHostContext = getHostContext();243    // TODO: Experiencing an error where oldProps is null. Suggests a host244    // component is hitting the resume path. Figure out why. Possibly245    // related to `hidden`.246    const updatePayload = prepareUpdate(247      instance,248      type,249      oldProps,250      newProps,251      rootContainerInstance,252      currentHostContext,253    );254    // TODO: Type this specific to this type of component.255    workInProgress.updateQueue = (updatePayload: any);256    // If the update payload indicates that there is a change or if there257    // is a new ref we mark this as an update. All the work is done in commitWork.258    if (updatePayload) {259      markUpdate(workInProgress);260    }261  };262  updateHostText = function(263    current: Fiber,264    workInProgress: Fiber,265    oldText: string,266    newText: string,267  ) {268    // If the text differs, mark it as an update. All the work in done in commitWork.269    if (oldText !== newText) {270      markUpdate(workInProgress);271    }272  };273} else if (supportsPersistence) {274  // Persistent host tree mode275  appendAllChildren = function(276    parent: Instance,277    workInProgress: Fiber,278    needsVisibilityToggle: boolean,279    isHidden: boolean,280  ) {281    // We only have the top Fiber that was created but we need recurse down its282    // children to find all the terminal nodes.283    let node = workInProgress.child;284    while (node !== null) {285      // eslint-disable-next-line no-labels286      branches: if (node.tag === HostComponent) {287        let instance = node.stateNode;288        if (needsVisibilityToggle && isHidden) {289          // This child is inside a timed out tree. Hide it.290          const props = node.memoizedProps;291          const type = node.type;292          instance = cloneHiddenInstance(instance, type, props, node);293        }294        appendInitialChild(parent, instance);295      } else if (node.tag === HostText) {296        let instance = node.stateNode;297        if (needsVisibilityToggle && isHidden) {298          // This child is inside a timed out tree. Hide it.299          const text = node.memoizedProps;300          instance = cloneHiddenTextInstance(instance, text, node);301        }302        appendInitialChild(parent, instance);303      } else if (node.tag === HostPortal) {304        // If we have a portal child, then we don't want to traverse305        // down its children. Instead, we'll get insertions from each child in306        // the portal directly.307      } else if (node.tag === SuspenseComponent) {308        if ((node.flags & Update) !== NoFlags) {309          // Need to toggle the visibility of the primary children.310          const newIsHidden = node.memoizedState !== null;311          if (newIsHidden) {312            const primaryChildParent = node.child;313            if (primaryChildParent !== null) {314              if (primaryChildParent.child !== null) {315                primaryChildParent.child.return = primaryChildParent;316                appendAllChildren(317                  parent,318                  primaryChildParent,319                  true,320                  newIsHidden,321                );322              }323              const fallbackChildParent = primaryChildParent.sibling;324              if (fallbackChildParent !== null) {325                fallbackChildParent.return = node;326                node = fallbackChildParent;327                continue;328              }329            }330          }331        }332        if (node.child !== null) {333          // Continue traversing like normal334          node.child.return = node;335          node = node.child;336          continue;337        }338      } else if (node.child !== null) {339        node.child.return = node;340        node = node.child;341        continue;342      }343      // $FlowFixMe This is correct but Flow is confused by the labeled break.344      node = (node: Fiber);345      if (node === workInProgress) {346        return;347      }348      while (node.sibling === null) {349        if (node.return === null || node.return === workInProgress) {350          return;351        }352        node = node.return;353      }354      node.sibling.return = node.return;355      node = node.sibling;356    }357  };358  // An unfortunate fork of appendAllChildren because we have two different parent types.359  const appendAllChildrenToContainer = function(360    containerChildSet: ChildSet,361    workInProgress: Fiber,362    needsVisibilityToggle: boolean,363    isHidden: boolean,364  ) {365    // We only have the top Fiber that was created but we need recurse down its366    // children to find all the terminal nodes.367    let node = workInProgress.child;368    while (node !== null) {369      // eslint-disable-next-line no-labels370      branches: if (node.tag === HostComponent) {371        let instance = node.stateNode;372        if (needsVisibilityToggle && isHidden) {373          // This child is inside a timed out tree. Hide it.374          const props = node.memoizedProps;375          const type = node.type;376          instance = cloneHiddenInstance(instance, type, props, node);377        }378        appendChildToContainerChildSet(containerChildSet, instance);379      } else if (node.tag === HostText) {380        let instance = node.stateNode;381        if (needsVisibilityToggle && isHidden) {382          // This child is inside a timed out tree. Hide it.383          const text = node.memoizedProps;384          instance = cloneHiddenTextInstance(instance, text, node);385        }386        appendChildToContainerChildSet(containerChildSet, instance);387      } else if (node.tag === HostPortal) {388        // If we have a portal child, then we don't want to traverse389        // down its children. Instead, we'll get insertions from each child in390        // the portal directly.391      } else if (node.tag === SuspenseComponent) {392        if ((node.flags & Update) !== NoFlags) {393          // Need to toggle the visibility of the primary children.394          const newIsHidden = node.memoizedState !== null;395          if (newIsHidden) {396            const primaryChildParent = node.child;397            if (primaryChildParent !== null) {398              if (primaryChildParent.child !== null) {399                primaryChildParent.child.return = primaryChildParent;400                appendAllChildrenToContainer(401                  containerChildSet,402                  primaryChildParent,403                  true,404                  newIsHidden,405                );406              }407              const fallbackChildParent = primaryChildParent.sibling;408              if (fallbackChildParent !== null) {409                fallbackChildParent.return = node;410                node = fallbackChildParent;411                continue;412              }413            }414          }415        }416        if (node.child !== null) {417          // Continue traversing like normal418          node.child.return = node;419          node = node.child;420          continue;421        }422      } else if (node.child !== null) {423        node.child.return = node;424        node = node.child;425        continue;426      }427      // $FlowFixMe This is correct but Flow is confused by the labeled break.428      node = (node: Fiber);429      if (node === workInProgress) {430        return;431      }432      while (node.sibling === null) {433        if (node.return === null || node.return === workInProgress) {434          return;435        }436        node = node.return;437      }438      node.sibling.return = node.return;439      node = node.sibling;440    }441  };442  updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {443    const portalOrRoot: {444      containerInfo: Container,445      pendingChildren: ChildSet,446      ...447    } = workInProgress.stateNode;448    const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);449    if (childrenUnchanged) {450      // No changes, just reuse the existing instance.451    } else {452      const container = portalOrRoot.containerInfo;453      const newChildSet = createContainerChildSet(container);454      // If children might have changed, we have to add them all to the set.455      appendAllChildrenToContainer(newChildSet, workInProgress, false, false);456      portalOrRoot.pendingChildren = newChildSet;457      // Schedule an update on the container to swap out the container.458      markUpdate(workInProgress);459      finalizeContainerChildren(container, newChildSet);460    }461  };462  updateHostComponent = function(463    current: Fiber,464    workInProgress: Fiber,465    type: Type,466    newProps: Props,467    rootContainerInstance: Container,468  ) {469    const currentInstance = current.stateNode;470    const oldProps = current.memoizedProps;471    // If there are no effects associated with this node, then none of our children had any updates.472    // This guarantees that we can reuse all of them.473    const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);474    if (childrenUnchanged && oldProps === newProps) {475      // No changes, just reuse the existing instance.476      // Note that this might release a previous clone.477      workInProgress.stateNode = currentInstance;478      return;479    }480    const recyclableInstance: Instance = workInProgress.stateNode;481    const currentHostContext = getHostContext();482    let updatePayload = null;483    if (oldProps !== newProps) {484      updatePayload = prepareUpdate(485        recyclableInstance,486        type,487        oldProps,488        newProps,489        rootContainerInstance,490        currentHostContext,491      );492    }493    if (childrenUnchanged && updatePayload === null) {494      // No changes, just reuse the existing instance.495      // Note that this might release a previous clone.496      workInProgress.stateNode = currentInstance;497      return;498    }499    const newInstance = cloneInstance(500      currentInstance,501      updatePayload,502      type,503      oldProps,504      newProps,505      workInProgress,506      childrenUnchanged,507      recyclableInstance,508    );509    if (510      finalizeInitialChildren(511        newInstance,512        type,513        newProps,514        rootContainerInstance,515        currentHostContext,516      )517    ) {518      markUpdate(workInProgress);519    }520    workInProgress.stateNode = newInstance;521    if (childrenUnchanged) {522      // If there are no other effects in this tree, we need to flag this node as having one.523      // Even though we're not going to use it for anything.524      // Otherwise parents won't know that there are new children to propagate upwards.525      markUpdate(workInProgress);526    } else {527      // If children might have changed, we have to add them all to the set.528      appendAllChildren(newInstance, workInProgress, false, false);529    }530  };531  updateHostText = function(532    current: Fiber,533    workInProgress: Fiber,534    oldText: string,535    newText: string,536  ) {537    if (oldText !== newText) {538      // If the text content differs, we'll create a new text instance for it.539      const rootContainerInstance = getRootHostContainer();540      const currentHostContext = getHostContext();541      workInProgress.stateNode = createTextInstance(542        newText,543        rootContainerInstance,544        currentHostContext,545        workInProgress,546      );547      // We'll have to mark it as having an effect, even though we won't use the effect for anything.548      // This lets the parents know that at least one of their children has changed.549      markUpdate(workInProgress);550    } else {551      workInProgress.stateNode = current.stateNode;552    }553  };554} else {555  // No host operations556  updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {557    // Noop558  };559  updateHostComponent = function(560    current: Fiber,561    workInProgress: Fiber,562    type: Type,563    newProps: Props,564    rootContainerInstance: Container,565  ) {566    // Noop567  };568  updateHostText = function(569    current: Fiber,570    workInProgress: Fiber,571    oldText: string,572    newText: string,573  ) {574    // Noop575  };576}577function cutOffTailIfNeeded(578  renderState: SuspenseListRenderState,579  hasRenderedATailFallback: boolean,580) {581  if (getIsHydrating()) {582    // If we're hydrating, we should consume as many items as we can583    // so we don't leave any behind.584    return;585  }586  switch (renderState.tailMode) {587    case 'hidden': {588      // Any insertions at the end of the tail list after this point589      // should be invisible. If there are already mounted boundaries590      // anything before them are not considered for collapsing.591      // Therefore we need to go through the whole tail to find if592      // there are any.593      let tailNode = renderState.tail;594      let lastTailNode = null;595      while (tailNode !== null) {596        if (tailNode.alternate !== null) {597          lastTailNode = tailNode;598        }599        tailNode = tailNode.sibling;600      }601      // Next we're simply going to delete all insertions after the602      // last rendered item.603      if (lastTailNode === null) {604        // All remaining items in the tail are insertions.605        renderState.tail = null;606      } else {607        // Detach the insertion after the last node that was already608        // inserted.609        lastTailNode.sibling = null;610      }611      break;612    }613    case 'collapsed': {614      // Any insertions at the end of the tail list after this point615      // should be invisible. If there are already mounted boundaries616      // anything before them are not considered for collapsing.617      // Therefore we need to go through the whole tail to find if618      // there are any.619      let tailNode = renderState.tail;620      let lastTailNode = null;621      while (tailNode !== null) {622        if (tailNode.alternate !== null) {623          lastTailNode = tailNode;624        }625        tailNode = tailNode.sibling;626      }627      // Next we're simply going to delete all insertions after the628      // last rendered item.629      if (lastTailNode === null) {630        // All remaining items in the tail are insertions.631        if (!hasRenderedATailFallback && renderState.tail !== null) {632          // We suspended during the head. We want to show at least one633          // row at the tail. So we'll keep on and cut off the rest.634          renderState.tail.sibling = null;635        } else {636          renderState.tail = null;637        }638      } else {639        // Detach the insertion after the last node that was already640        // inserted.641        lastTailNode.sibling = null;642      }643      break;644    }645  }646}647function bubbleProperties(completedWork: Fiber) {648  const didBailout =649    completedWork.alternate !== null &&650    completedWork.alternate.child === completedWork.child;651  let newChildLanes = NoLanes;652  let subtreeFlags = NoFlags;653  if (!didBailout) {654    // Bubble up the earliest expiration time.655    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {656      // In profiling mode, resetChildExpirationTime is also used to reset657      // profiler durations.658      let actualDuration = completedWork.actualDuration;659      let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);660      let child = completedWork.child;661      while (child !== null) {662        newChildLanes = mergeLanes(663          newChildLanes,664          mergeLanes(child.lanes, child.childLanes),665        );666        subtreeFlags |= child.subtreeFlags;667        subtreeFlags |= child.flags;668        // When a fiber is cloned, its actualDuration is reset to 0. This value will669        // only be updated if work is done on the fiber (i.e. it doesn't bailout).670        // When work is done, it should bubble to the parent's actualDuration. If671        // the fiber has not been cloned though, (meaning no work was done), then672        // this value will reflect the amount of time spent working on a previous673        // render. In that case it should not bubble. We determine whether it was674        // cloned by comparing the child pointer.675        actualDuration += child.actualDuration;676        treeBaseDuration += child.treeBaseDuration;677        child = child.sibling;678      }679      completedWork.actualDuration = actualDuration;680      completedWork.treeBaseDuration = treeBaseDuration;681    } else {682      let child = completedWork.child;683      while (child !== null) {684        newChildLanes = mergeLanes(685          newChildLanes,686          mergeLanes(child.lanes, child.childLanes),687        );688        subtreeFlags |= child.subtreeFlags;689        subtreeFlags |= child.flags;690        // Update the return pointer so the tree is consistent. This is a code691        // smell because it assumes the commit phase is never concurrent with692        // the render phase. Will address during refactor to alternate model.693        child.return = completedWork;694        child = child.sibling;695      }696    }697    completedWork.subtreeFlags |= subtreeFlags;698  } else {699    // Bubble up the earliest expiration time.700    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {701      // In profiling mode, resetChildExpirationTime is also used to reset702      // profiler durations.703      let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);704      let child = completedWork.child;705      while (child !== null) {706        newChildLanes = mergeLanes(707          newChildLanes,708          mergeLanes(child.lanes, child.childLanes),709        );710        // "Static" flags share the lifetime of the fiber/hook they belong to,711        // so we should bubble those up even during a bailout. All the other712        // flags have a lifetime only of a single render + commit, so we should713        // ignore them.714        subtreeFlags |= child.subtreeFlags & StaticMask;715        subtreeFlags |= child.flags & StaticMask;716        treeBaseDuration += child.treeBaseDuration;717        child = child.sibling;718      }719      completedWork.treeBaseDuration = treeBaseDuration;720    } else {721      let child = completedWork.child;722      while (child !== null) {723        newChildLanes = mergeLanes(724          newChildLanes,725          mergeLanes(child.lanes, child.childLanes),726        );727        // "Static" flags share the lifetime of the fiber/hook they belong to,728        // so we should bubble those up even during a bailout. All the other729        // flags have a lifetime only of a single render + commit, so we should730        // ignore them.731        subtreeFlags |= child.subtreeFlags & StaticMask;732        subtreeFlags |= child.flags & StaticMask;733        // Update the return pointer so the tree is consistent. This is a code734        // smell because it assumes the commit phase is never concurrent with735        // the render phase. Will address during refactor to alternate model.736        child.return = completedWork;737        child = child.sibling;738      }739    }740    completedWork.subtreeFlags |= subtreeFlags;741  }742  completedWork.childLanes = newChildLanes;743  return didBailout;744}745function completeWork(746  current: Fiber | null,747  workInProgress: Fiber,748  renderLanes: Lanes,749): Fiber | null {750  const newProps = workInProgress.pendingProps;751  switch (workInProgress.tag) {752    case IndeterminateComponent:753    case LazyComponent:754    case SimpleMemoComponent:755    case FunctionComponent:756    case ForwardRef:757    case Fragment:758    case Mode:759    case Profiler:760    case ContextConsumer:761    case MemoComponent:762      bubbleProperties(workInProgress);763      return null;764    case ClassComponent: {765      const Component = workInProgress.type;766      if (isLegacyContextProvider(Component)) {767        popLegacyContext(workInProgress);768      }769      bubbleProperties(workInProgress);770      return null;771    }772    case HostRoot: {773      const fiberRoot = (workInProgress.stateNode: FiberRoot);774      if (enableCache) {775        popRootCachePool(fiberRoot, renderLanes);776        const cache: Cache = workInProgress.memoizedState.cache;777        popCacheProvider(workInProgress, cache);778      }779      popHostContainer(workInProgress);780      popTopLevelLegacyContextObject(workInProgress);781      resetMutableSourceWorkInProgressVersions();782      if (fiberRoot.pendingContext) {783        fiberRoot.context = fiberRoot.pendingContext;784        fiberRoot.pendingContext = null;785      }786      if (current === null || current.child === null) {787        // If we hydrated, pop so that we can delete any remaining children788        // that weren't hydrated.789        const wasHydrated = popHydrationState(workInProgress);790        if (wasHydrated) {791          // If we hydrated, then we'll need to schedule an update for792          // the commit side-effects on the root.793          markUpdate(workInProgress);794        } else if (!fiberRoot.hydrate) {795          // Schedule an effect to clear this container at the start of the next commit.796          // This handles the case of React rendering into a container with previous children.797          // It's also safe to do for updates too, because current.child would only be null798          // if the previous render was null (so the the container would already be empty).799          workInProgress.flags |= Snapshot;800        }801      }802      updateHostContainer(current, workInProgress);803      bubbleProperties(workInProgress);804      return null;805    }806    case HostComponent: {807      popHostContext(workInProgress);808      const rootContainerInstance = getRootHostContainer();809      const type = workInProgress.type;810      if (current !== null && workInProgress.stateNode != null) {811        updateHostComponent(812          current,813          workInProgress,814          type,815          newProps,816          rootContainerInstance,817        );818        if (current.ref !== workInProgress.ref) {819          markRef(workInProgress);820        }821      } else {822        if (!newProps) {823          invariant(824            workInProgress.stateNode !== null,825            'We must have new props for new mounts. This error is likely ' +826              'caused by a bug in React. Please file an issue.',827          );828          // This can happen when we abort work.829          bubbleProperties(workInProgress);830          return null;831        }832        const currentHostContext = getHostContext();833        // TODO: Move createInstance to beginWork and keep it on a context834        // "stack" as the parent. Then append children as we go in beginWork835        // or completeWork depending on whether we want to add them top->down or836        // bottom->up. Top->down is faster in IE11.837        const wasHydrated = popHydrationState(workInProgress);838        if (wasHydrated) {839          // TODO: Move this and createInstance step into the beginPhase840          // to consolidate.841          if (842            prepareToHydrateHostInstance(843              workInProgress,844              rootContainerInstance,845              currentHostContext,846            )847          ) {848            // If changes to the hydrated node need to be applied at the849            // commit-phase we mark this as such.850            markUpdate(workInProgress);851          }852        } else {853          const instance = createInstance(854            type,855            newProps,856            rootContainerInstance,857            currentHostContext,858            workInProgress,859          );860          appendAllChildren(instance, workInProgress, false, false);861          workInProgress.stateNode = instance;862          // Certain renderers require commit-time effects for initial mount.863          // (eg DOM renderer supports auto-focus for certain elements).864          // Make sure such renderers get scheduled for later work.865          if (866            finalizeInitialChildren(867              instance,868              type,869              newProps,870              rootContainerInstance,871              currentHostContext,872            )873          ) {874            markUpdate(workInProgress);875          }876        }877        if (workInProgress.ref !== null) {878          // If there is a ref on a host node we need to schedule a callback879          markRef(workInProgress);880        }881      }882      bubbleProperties(workInProgress);883      return null;884    }885    case HostText: {886      const newText = newProps;887      if (current && workInProgress.stateNode != null) {888        const oldText = current.memoizedProps;889        // If we have an alternate, that means this is an update and we need890        // to schedule a side-effect to do the updates.891        updateHostText(current, workInProgress, oldText, newText);892      } else {893        if (typeof newText !== 'string') {894          invariant(895            workInProgress.stateNode !== null,896            'We must have new props for new mounts. This error is likely ' +897              'caused by a bug in React. Please file an issue.',898          );899          // This can happen when we abort work.900        }901        const rootContainerInstance = getRootHostContainer();902        const currentHostContext = getHostContext();903        const wasHydrated = popHydrationState(workInProgress);904        if (wasHydrated) {905          if (prepareToHydrateHostTextInstance(workInProgress)) {906            markUpdate(workInProgress);907          }908        } else {909          workInProgress.stateNode = createTextInstance(910            newText,911            rootContainerInstance,912            currentHostContext,913            workInProgress,914          );915        }916      }917      bubbleProperties(workInProgress);918      return null;919    }920    case SuspenseComponent: {921      popSuspenseContext(workInProgress);922      const nextState: null | SuspenseState = workInProgress.memoizedState;923      if (enableSuspenseServerRenderer) {924        if (nextState !== null && nextState.dehydrated !== null) {925          if (current === null) {926            const wasHydrated = popHydrationState(workInProgress);927            invariant(928              wasHydrated,929              'A dehydrated suspense component was completed without a hydrated node. ' +930                'This is probably a bug in React.',931            );932            prepareToHydrateHostSuspenseInstance(workInProgress);933            bubbleProperties(workInProgress);934            if (enableProfilerTimer) {935              if ((workInProgress.mode & ProfileMode) !== NoMode) {936                const isTimedOutSuspense = nextState !== null;937                if (isTimedOutSuspense) {938                  // Don't count time spent in a timed out Suspense subtree as part of the base duration.939                  const primaryChildFragment = workInProgress.child;940                  if (primaryChildFragment !== null) {941                    // $FlowFixMe Flow doesn't support type casting in combination with the -= operator942                    workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);943                  }944                }945              }946            }947            return null;948          } else {949            // We should never have been in a hydration state if we didn't have a current.950            // However, in some of those paths, we might have reentered a hydration state951            // and then we might be inside a hydration state. In that case, we'll need to exit out of it.952            resetHydrationState();953            if ((workInProgress.flags & DidCapture) === NoFlags) {954              // This boundary did not suspend so it's now hydrated and unsuspended.955              workInProgress.memoizedState = null;956            }957            // If nothing suspended, we need to schedule an effect to mark this boundary958            // as having hydrated so events know that they're free to be invoked.959            // It's also a signal to replay events and the suspense callback.960            // If something suspended, schedule an effect to attach retry listeners.961            // So we might as well always mark this.962            workInProgress.flags |= Update;963            bubbleProperties(workInProgress);964            if (enableProfilerTimer) {965              if ((workInProgress.mode & ProfileMode) !== NoMode) {966                const isTimedOutSuspense = nextState !== null;967                if (isTimedOutSuspense) {968                  // Don't count time spent in a timed out Suspense subtree as part of the base duration.969                  const primaryChildFragment = workInProgress.child;970                  if (primaryChildFragment !== null) {971                    // $FlowFixMe Flow doesn't support type casting in combination with the -= operator972                    workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);973                  }974                }975              }976            }977            return null;978          }979        }980      }981      if ((workInProgress.flags & DidCapture) !== NoFlags) {982        // Something suspended. Re-render with the fallback children.983        workInProgress.lanes = renderLanes;984        // Do not reset the effect list.985        if (986          enableProfilerTimer &&987          (workInProgress.mode & ProfileMode) !== NoMode988        ) {989          transferActualDuration(workInProgress);990        }991        // Don't bubble properties in this case.992        return workInProgress;993      }994      const nextDidTimeout = nextState !== null;995      let prevDidTimeout = false;996      if (current === null) {997        if (workInProgress.memoizedProps.fallback !== undefined) {998          popHydrationState(workInProgress);999        }1000      } else {1001        const prevState: null | SuspenseState = current.memoizedState;1002        prevDidTimeout = prevState !== null;1003      }1004      if (nextDidTimeout && !prevDidTimeout) {1005        // TODO: This will still suspend a synchronous tree if anything1006        // in the concurrent tree already suspended during this render.1007        // This is a known bug.1008        if ((workInProgress.mode & ConcurrentMode) !== NoMode) {1009          // TODO: Move this back to throwException because this is too late1010          // if this is a large tree which is common for initial loads. We1011          // don't know if we should restart a render or not until we get1012          // this marker, and this is too late.1013          // If this render already had a ping or lower pri updates,1014          // and this is the first time we know we're going to suspend we1015          // should be able to immediately restart from within throwException.1016          const hasInvisibleChildContext =1017            current === null &&1018            workInProgress.memoizedProps.unstable_avoidThisFallback !== true;1019          if (1020            hasInvisibleChildContext ||1021            hasSuspenseContext(1022              suspenseStackCursor.current,1023              (InvisibleParentSuspenseContext: SuspenseContext),1024            )1025          ) {1026            // If this was in an invisible tree or a new render, then showing1027            // this boundary is ok.1028            renderDidSuspend();1029          } else {1030            // Otherwise, we're going to have to hide content so we should1031            // suspend for longer if possible.1032            renderDidSuspendDelayIfPossible();1033          }1034        }1035      }1036      if (supportsPersistence) {1037        // TODO: Only schedule updates if not prevDidTimeout.1038        if (nextDidTimeout) {1039          // If this boundary just timed out, schedule an effect to attach a1040          // retry listener to the promise. This flag is also used to hide the1041          // primary children.1042          workInProgress.flags |= Update;1043        }1044      }1045      if (supportsMutation) {1046        // TODO: Only schedule updates if these values are non equal, i.e. it changed.1047        if (nextDidTimeout || prevDidTimeout) {1048          // If this boundary just timed out, schedule an effect to attach a1049          // retry listener to the promise. This flag is also used to hide the1050          // primary children. In mutation mode, we also need the flag to1051          // *unhide* children that were previously hidden, so check if this1052          // is currently timed out, too.1053          workInProgress.flags |= Update;1054        }1055      }1056      if (1057        enableSuspenseCallback &&1058        workInProgress.updateQueue !== null &&1059        workInProgress.memoizedProps.suspenseCallback != null1060      ) {1061        // Always notify the callback1062        workInProgress.flags |= Update;1063      }1064      bubbleProperties(workInProgress);1065      if (enableProfilerTimer) {1066        if ((workInProgress.mode & ProfileMode) !== NoMode) {1067          if (nextDidTimeout) {1068            // Don't count time spent in a timed out Suspense subtree as part of the base duration.1069            const primaryChildFragment = workInProgress.child;1070            if (primaryChildFragment !== null) {1071              // $FlowFixMe Flow doesn't support type casting in combination with the -= operator1072              workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);1073            }1074          }1075        }1076      }1077      return null;1078    }1079    case HostPortal:1080      popHostContainer(workInProgress);1081      updateHostContainer(current, workInProgress);1082      if (current === null) {1083        preparePortalMount(workInProgress.stateNode.containerInfo);1084      }1085      bubbleProperties(workInProgress);1086      return null;1087    case ContextProvider:1088      // Pop provider fiber1089      const context: ReactContext<any> = workInProgress.type._context;1090      popProvider(context, workInProgress);1091      bubbleProperties(workInProgress);1092      return null;1093    case IncompleteClassComponent: {1094      // Same as class component case. I put it down here so that the tags are1095      // sequential to ensure this switch is compiled to a jump table.1096      const Component = workInProgress.type;1097      if (isLegacyContextProvider(Component)) {1098        popLegacyContext(workInProgress);1099      }1100      bubbleProperties(workInProgress);1101      return null;1102    }1103    case SuspenseListComponent: {1104      popSuspenseContext(workInProgress);1105      const renderState: null | SuspenseListRenderState =1106        workInProgress.memoizedState;1107      if (renderState === null) {1108        // We're running in the default, "independent" mode.1109        // We don't do anything in this mode.1110        bubbleProperties(workInProgress);1111        return null;1112      }1113      let didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;1114      const renderedTail = renderState.rendering;1115      if (renderedTail === null) {1116        // We just rendered the head.1117        if (!didSuspendAlready) {1118          // This is the first pass. We need to figure out if anything is still1119          // suspended in the rendered set.1120          // If new content unsuspended, but there's still some content that1121          // didn't. Then we need to do a second pass that forces everything1122          // to keep showing their fallbacks.1123          // We might be suspended if something in this render pass suspended, or1124          // something in the previous committed pass suspended. Otherwise,1125          // there's no chance so we can skip the expensive call to1126          // findFirstSuspended.1127          const cannotBeSuspended =1128            renderHasNotSuspendedYet() &&1129            (current === null || (current.flags & DidCapture) === NoFlags);1130          if (!cannotBeSuspended) {1131            let row = workInProgress.child;1132            while (row !== null) {1133              const suspended = findFirstSuspended(row);1134              if (suspended !== null) {1135                didSuspendAlready = true;1136                workInProgress.flags |= DidCapture;1137                cutOffTailIfNeeded(renderState, false);1138                // If this is a newly suspended tree, it might not get committed as1139                // part of the second pass. In that case nothing will subscribe to1140                // its thennables. Instead, we'll transfer its thennables to the1141                // SuspenseList so that it can retry if they resolve.1142                // There might be multiple of these in the list but since we're1143                // going to wait for all of them anyway, it doesn't really matter1144                // which ones gets to ping. In theory we could get clever and keep1145                // track of how many dependencies remain but it gets tricky because1146                // in the meantime, we can add/remove/change items and dependencies.1147                // We might bail out of the loop before finding any but that1148                // doesn't matter since that means that the other boundaries that1149                // we did find already has their listeners attached.1150                const newThennables = suspended.updateQueue;1151                if (newThennables !== null) {1152                  workInProgress.updateQueue = newThennables;1153                  workInProgress.flags |= Update;1154                }1155                // Rerender the whole list, but this time, we'll force fallbacks1156                // to stay in place.1157                // Reset the effect flags before doing the second pass since that's now invalid.1158                // Reset the child fibers to their original state.1159                workInProgress.subtreeFlags = NoFlags;1160                resetChildFibers(workInProgress, renderLanes);1161                // Set up the Suspense Context to force suspense and immediately1162                // rerender the children.1163                pushSuspenseContext(1164                  workInProgress,1165                  setShallowSuspenseContext(1166                    suspenseStackCursor.current,1167                    ForceSuspenseFallback,1168                  ),1169                );1170                // Don't bubble properties in this case.1171                return workInProgress.child;1172              }1173              row = row.sibling;1174            }1175          }1176          if (renderState.tail !== null && now() > getRenderTargetTime()) {1177            // We have already passed our CPU deadline but we still have rows1178            // left in the tail. We'll just give up further attempts to render1179            // the main content and only render fallbacks.1180            workInProgress.flags |= DidCapture;1181            didSuspendAlready = true;1182            cutOffTailIfNeeded(renderState, false);1183            // Since nothing actually suspended, there will nothing to ping this1184            // to get it started back up to attempt the next item. While in terms1185            // of priority this work has the same priority as this current render,1186            // it's not part of the same transition once the transition has1187            // committed. If it's sync, we still want to yield so that it can be1188            // painted. Conceptually, this is really the same as pinging.1189            // We can use any RetryLane even if it's the one currently rendering1190            // since we're leaving it behind on this node.1191            workInProgress.lanes = SomeRetryLane;1192          }1193        } else {1194          cutOffTailIfNeeded(renderState, false);1195        }1196        // Next we're going to render the tail.1197      } else {1198        // Append the rendered row to the child list.1199        if (!didSuspendAlready) {1200          const suspended = findFirstSuspended(renderedTail);1201          if (suspended !== null) {1202            workInProgress.flags |= DidCapture;1203            didSuspendAlready = true;1204            // Ensure we transfer the update queue to the parent so that it doesn't1205            // get lost if this row ends up dropped during a second pass.1206            const newThennables = suspended.updateQueue;1207            if (newThennables !== null) {1208              workInProgress.updateQueue = newThennables;1209              workInProgress.flags |= Update;1210            }1211            cutOffTailIfNeeded(renderState, true);1212            // This might have been modified.1213            if (1214              renderState.tail === null &&1215              renderState.tailMode === 'hidden' &&1216              !renderedTail.alternate &&1217              !getIsHydrating() // We don't cut it if we're hydrating.1218            ) {1219              // We're done.1220              bubbleProperties(workInProgress);1221              return null;1222            }1223          } else if (1224            // The time it took to render last row is greater than the remaining1225            // time we have to render. So rendering one more row would likely1226            // exceed it.1227            now() * 2 - renderState.renderingStartTime >1228              getRenderTargetTime() &&1229            renderLanes !== OffscreenLane1230          ) {1231            // We have now passed our CPU deadline and we'll just give up further1232            // attempts to render the main content and only render fallbacks.1233            // The assumption is that this is usually faster.1234            workInProgress.flags |= DidCapture;1235            didSuspendAlready = true;1236            cutOffTailIfNeeded(renderState, false);1237            // Since nothing actually suspended, there will nothing to ping this1238            // to get it started back up to attempt the next item. While in terms1239            // of priority this work has the same priority as this current render,1240            // it's not part of the same transition once the transition has1241            // committed. If it's sync, we still want to yield so that it can be1242            // painted. Conceptually, this is really the same as pinging.1243            // We can use any RetryLane even if it's the one currently rendering1244            // since we're leaving it behind on this node.1245            workInProgress.lanes = SomeRetryLane;1246          }1247        }1248        if (renderState.isBackwards) {1249          // The effect list of the backwards tail will have been added1250          // to the end. This breaks the guarantee that life-cycles fire in1251          // sibling order but that isn't a strong guarantee promised by React.1252          // Especially since these might also just pop in during future commits.1253          // Append to the beginning of the list.1254          renderedTail.sibling = workInProgress.child;1255          workInProgress.child = renderedTail;1256        } else {1257          const previousSibling = renderState.last;1258          if (previousSibling !== null) {1259            previousSibling.sibling = renderedTail;1260          } else {1261            workInProgress.child = renderedTail;1262          }1263          renderState.last = renderedTail;1264        }1265      }1266      if (renderState.tail !== null) {1267        // We still have tail rows to render.1268        // Pop a row.1269        const next = renderState.tail;1270        renderState.rendering = next;1271        renderState.tail = next.sibling;1272        renderState.renderingStartTime = now();1273        next.sibling = null;1274        // Restore the context.1275        // TODO: We can probably just avoid popping it instead and only1276        // setting it the first time we go from not suspended to suspended.1277        let suspenseContext = suspenseStackCursor.current;1278        if (didSuspendAlready) {1279          suspenseContext = setShallowSuspenseContext(1280            suspenseContext,1281            ForceSuspenseFallback,1282          );1283        } else {1284          suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);1285        }1286        pushSuspenseContext(workInProgress, suspenseContext);1287        // Do a pass over the next row.1288        // Don't bubble properties in this case.1289        return next;1290      }1291      bubbleProperties(workInProgress);1292      return null;1293    }1294    case ScopeComponent: {1295      if (enableScopeAPI) {1296        if (current === null) {1297          const scopeInstance: ReactScopeInstance = createScopeInstance();1298          workInProgress.stateNode = scopeInstance;1299          prepareScopeUpdate(scopeInstance, workInProgress);1300          if (workInProgress.ref !== null) {1301            markRef(workInProgress);1302            markUpdate(workInProgress);1303          }1304        } else {1305          if (workInProgress.ref !== null) {1306            markUpdate(workInProgress);1307          }1308          if (current.ref !== workInProgress.ref) {1309            markRef(workInProgress);1310          }1311        }1312        bubbleProperties(workInProgress);1313        return null;1314      }1315      break;1316    }1317    case OffscreenComponent:1318    case LegacyHiddenComponent: {1319      popRenderLanes(workInProgress);1320      const nextState: OffscreenState | null = workInProgress.memoizedState;1321      const nextIsHidden = nextState !== null;1322      if (current !== null) {1323        const prevState: OffscreenState | null = current.memoizedState;1324        const prevIsHidden = prevState !== null;1325        if (1326          prevIsHidden !== nextIsHidden &&1327          newProps.mode !== 'unstable-defer-without-hiding'1328        ) {1329          workInProgress.flags |= Update;1330        }1331      }1332      // Don't bubble properties for hidden children.1333      if (1334        !nextIsHidden ||1335        includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) ||1336        (workInProgress.mode & ConcurrentMode) === NoMode1337      ) {1338        bubbleProperties(workInProgress);1339      }1340      if (enableCache) {1341        const spawnedCachePool: SpawnedCachePool | null = (workInProgress.updateQueue: any);1342        if (spawnedCachePool !== null) {1343          popCachePool(workInProgress);1344        }1345      }1346      return null;1347    }1348    case CacheComponent: {1349      if (enableCache) {1350        const cache: Cache = workInProgress.memoizedState.cache;1351        popCacheProvider(workInProgress, cache);1352        bubbleProperties(workInProgress);1353        return null;1354      }1355    }1356  }1357  invariant(1358    false,1359    'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +1360      'React. Please file an issue.',1361    workInProgress.tag,1362  );1363}...ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js  
...280}281// å°child fiberçexpirationTimeåæ³¡å°ç¶çº§282// è¿æ ·å¨ç¶çº§å°±è½ç´å°ååä¸ä¼å
级æé«å°expirationTime283// é
å bailoutOnAlreadyFinishedWork çä¼åè·¯å¾284function resetChildExpirationTime(completedWork) {285  let newChildExpirationTime = NoWork;286  let child = completedWork.child;287  while (child) {288    const childUpdateExpirationTime = child.expirationTime;289    const childChildExpirationTime = child.childChildExpirationTime;290    if (childUpdateExpirationTime > newChildExpirationTime) {291      newChildExpirationTime = childUpdateExpirationTime;292    }293    if (childChildExpirationTime > newChildExpirationTime) {294      newChildExpirationTime = childChildExpirationTime;295    }296    child = child.sibling;297  }298  completedWork.childExpirationTime = newChildExpirationTime;299}300// ç±äºä¸å®æ¯beginWorkè¿ånullæä¼æ§è¡completeUnitOfWorkï¼èbeginWorkå§ç»å建并è¿åfiber.child301// æä»¥ä¼ å
¥çfiberä¸å®æ¯æä¸ªåæ çå¶åèç¹302// è¿åèç¹çå
å¼èç¹ï¼å¦æåå¨ï¼ï¼ä¸åå¨å
å¼èç¹æ¶éå½ä¸ä¸çº§303function completeUnitOfWork(unitOfWork) {304  workInProgress = unitOfWork;305  do {306    const current = workInProgress.alternate;307    const returnFiber = workInProgress.return;308    // if (!(workInProgress.effectTag & Incomplete)) {309    if (true) {310      // 该fiberæªæåºé误311      // å½åæ»ä¼è¿ånull312      let next = completeWork(current, workInProgress);313      resetChildExpirationTime(workInProgress);314      if (next) {315        return next;316      }317      if (returnFiber) {318      // if (returnFiber && !(returnFiber.effectTag & Incomplete)) {319        // å°å®æçfiberç effect list appendå°ç¶çº§fiberä¸320        // è¿æ ·ä¸çº§çº§éå½ä¸å»åï¼æ ¹èç¹ä¼æä¸æ¡æ¬æ¬¡updateæææeffectçfiberçlist321        // 卿§è¡DOMæä½æ¶åªéè¦éåè¿æ¡é¾è¡¨èä¸éè¦åéå½ä¸éæ´ä¸ªfiberæ å°±è½æ§è¡effect对åºDOMæä½322        if (!returnFiber.firstEffect) {323          returnFiber.firstEffect = workInProgress.firstEffect;324        }325        if (workInProgress.lastEffect) {326          if (returnFiber.lastEffect) {327            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;...FiberScheduler.js
Source:FiberScheduler.js  
...331    let current = workInProgress.alternate;332    let next = completeWork(current, workInProgress, nextRenderExpirationTime);333    let returnFiber = workInProgress.return;334    let siblingFiber = workInProgress.sibling;335    resetChildExpirationTime(workInProgress, nextRenderExpirationTime);336    if (next != null) {337      return next;338    }339    if (returnFiber != null) {340      if (returnFiber.firstEffect == null) {341        returnFiber.firstEffect = workInProgress.firstEffect;342      }343      if (workInProgress.lastEffect != null) {344        if (returnFiber.lastEffect != null) {345          returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;346        }347        returnFiber.lastEffect = workInProgress.lastEffect;348      }349      let effectTag = workInProgress.effectTag;350      if (effectTag > PerformedWork) {351        if (returnFiber.lastEffect != null) {352          returnFiber.lastEffect.nextEffect = workInProgress;353        } else {354          returnFiber.firstEffect = workInProgress;355        }356        returnFiber.lastEffect = workInProgress;357      }358    }359    if (siblingFiber != null) {360      return siblingFiber;361    } else if (returnFiber != null) {362      workInProgress = returnFiber;363      continue;364    } else {365      const root = workInProgress.stateNode;366      root.isReadyForCommit = true;367      return null;368    }369  }370}371function commitRoot(finishedWork) {372  isWorking = true;373  isCommitting = true;374  let root = finishedWork.stateNode;375  root.isReadyForCommit = false;376  let firstEffect = null;377  if (finishedWork.effectTag > PerformedWork) {378    if (finishedWork.lastEffect != null) {379      finishedWork.lastEffect.nextEffect = finishedWork;380      firstEffect = finishedWork.firstEffect;381    } else {382      firstEffect = finishedWork;383    }384  } else {385    firstEffect = finishedWork.firstEffect;386  }387  nextEffect = firstEffect;388  while (nextEffect != null) {389    commitAllHostEffects();390  }391  root.current = finishedWork;392  isCommitting = false;393  isWorking = false;394  return root.current.expirationTime;395}396function commitAllHostEffects() {397  while (nextEffect != null) {398    let effectTag = nextEffect.effectTag;399    let primaryEffectTag = effectTag & ~PerformedWork;400    switch (primaryEffectTag) {401      case Placement: {402        commitPlacement(nextEffect);403        nextEffect.effectTag &= ~Placement;404        break;405      }406      case PlacementAndUpdate: {407        commitPlacement(nextEffect);408        nextEffect.effectTag &= ~Placement;409        commitWork(nextEffect);410        break;411      }412      case Update: {413        commitWork(nextEffect);414        break;415      }416      case Deletion: {417        commitDeletion(nextEffect);418        break;419      }420    }421    nextEffect = nextEffect.nextEffect;422  }423}424function computeAsyncExpiration(currentTime) {425  // Given the current clock time, returns an expiration time. We use rounding426  // to batch like updates together.427  // Should complete within ~1000ms. 1200ms max.428  const expirationMs = 5000;429  const bucketSizeMs = 250;430  return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs);431}432function computeInteractiveExpiration(currentTime) {433  // Should complete within ~500ms. 600ms max.434  const expirationMs = 500;435  const bucketSizeMs = 100;436  return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs);437}438export function recalculateCurrentTime() {439  mostRecentCurrentTime = msToExpirationTime(performance.now());440  return mostRecentCurrentTime;441}442export function computeExpirationForFiber(fiber) {443  let expirationTime;444  if (expirationContext !== NoWork) {445    // An explicit expiration context was set;446    expirationTime = expirationContext;447  } else if (isWorking) {448    if (isCommitting) {449      // Updates that occur during the commit phase should have sync priority450      // by default.451      expirationTime = Sync;452    } else {453      // Updates during the render phase should expire at the same time as454      // the work that is being rendered.455      expirationTime = nextRenderExpirationTime;456    }457  } else {458    // No explicit expiration context was set, and we're not currently459    // performing work. Calculate a new expiration time.460    if (fiber.mode & AsyncMode) {461      if (isBatchingInteractiveUpdates) {462        // This is an interactive update463        const currentTime = recalculateCurrentTime();464        expirationTime = computeInteractiveExpiration(currentTime);465      } else {466        // This is an async update467        const currentTime = recalculateCurrentTime();468        expirationTime = computeAsyncExpiration(currentTime);469      }470    } else {471      // This is a sync update472      expirationTime = Sync;473    }474  }475  if (isBatchingInteractiveUpdates) {476    // This is an interactive update. Keep track of the lowest pending477    // interactive expiration time. This allows us to synchronously flush478    // all interactive updates when needed.479    if (480      lowestPendingInteractiveExpirationTime === NoWork ||481      expirationTime > lowestPendingInteractiveExpirationTime482    ) {483      lowestPendingInteractiveExpirationTime = expirationTime;484    }485  }486  return expirationTime;487}488function resetChildExpirationTime(workInProgress, renderTime) {489  if (renderTime !== Never && workInProgress.expirationTime === Never) {490    // The children of this component are hidden. Don't bubble their491    // expiration times.492    return;493  }494  let newChildExpirationTime = NoWork;495  let child = workInProgress.child;496  while (child != null) {497    const childUpdateExpirationTime = child.expirationTime;498    const childChildExpirationTime = child.childExpirationTime;499    if (childUpdateExpirationTime > newChildExpirationTime) {500      newChildExpirationTime = childUpdateExpirationTime;501    }502    if (childChildExpirationTime > newChildExpirationTime) {...ReactFiberScheduler.js
Source:ReactFiberScheduler.js  
...128        // We're out of completion phase so replaying is fine now.129        mayReplayFailedUnitOfWork = true;130      }131      stopWorkTimer(workInProgress);132      resetChildExpirationTime(workInProgress, nextRenderExpirationTime);133      if (__DEV__) {134        resetCurrentFiber();135      }136      if (nextUnitOfWork !== null) {137        // Completing this fiber spawned new work. Work on that next.138        return nextUnitOfWork;139      }140      if (141        returnFiber !== null &&142        // Do not append effects to parents if a sibling failed to complete143        (returnFiber.effectTag & Incomplete) === NoEffect144      ) {145        // Append all the effects of the subtree and this fiber onto the effect146        // list of the parent. The completion order of the children affects the...simple_scheduler.js
Source:simple_scheduler.js  
...65          nextRenderExpirationTime,66        );67      }68      stopWorkTimer(workInProgress);69      resetChildExpirationTime(workInProgress, nextRenderExpirationTime);70      if (nextUnitOfWork !== null) {71        // Completing this fiber spawned new work. Work on that next.72        return nextUnitOfWork;73      }74      // ä¸é¢ç两个ifæ¯ææeffectlistçå
³é®ï¼ä»ä¸å¾ä¸æè½½ã75      // ï¼1ï¼æå½åFiberèç¹çåèç¹effectæå¨å°å½åFiberçç¶èç¹ãå½åFiberç¸å½äºä¸ä¸ªä¸é´åéï¼æ²éå
¶ç¶äº²åå
¶å¿åã76      if (77        returnFiber !== null &&78        // Do not append effects to parents if a sibling failed to complete79        (returnFiber.effectTag & Incomplete) === NoEffect80      ) {81        // Append all the effects of the subtree and this fiber onto the effect82        // list of the parent. The completion order of the children affects the83        // side-effect order....completeUnitOfWork.js
Source:completeUnitOfWork.js  
...17              stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);18          }19          stopWorkTimer(workInProgress);20          resetCurrentDebugFiberInDEV();21          resetChildExpirationTime(workInProgress);22          if (next !== null) {23              // Completing this fiber spawned new work. Work on that next.24              return next;25          }26          if (27              returnFiber !== null &&28              // Do not append effects to parents if a sibling failed to complete29              (returnFiber.effectTag & Incomplete) === NoEffect30          ) {31              // Append all the effects of the subtree and this fiber onto the effect32              // list of the parent. The completion order of the children affects the33              // side-effect order.34              if (returnFiber.firstEffect === null) {35                  returnFiber.firstEffect = workInProgress.firstEffect;...Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  const elementHandle = await page.$('text=Playwright is a Node.js library to automate');7  await page.resetChildExpirationTime(elementHandle);8  await browser.close();9})();10const { chromium } = require('playwright');11(async () => {12  const browser = await chromium.launch();13  const context = await browser.newContext();14  const page = await context.newPage();15  const elementHandle = await page.$('text=Playwright is a Node.js library to automate');16  await page.resetChildExpirationTime(elementHandle, 10000);17  await browser.close();18})();19const { chromium } = require('playwright');20(async () => {21  const browser = await chromium.launch();22  const context = await browser.newContext();23  const page = await context.newPage();24  const elementHandle = await page.$('text=Playwright is a Node.js library to automate');25  await page.resetChildExpirationTime(elementHandle, 60000);26  await browser.close();27})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.waitForTimeout(10000);7  await context.resetChildExpirationTime();8  await page.waitForTimeout(10000);9  await context.resetChildExpirationTime();10  await page.waitForTimeout(10000);11  await context.resetChildExpirationTime();12  await page.waitForTimeout(10000);13  await context.resetChildExpirationTime();14  await page.waitForTimeout(10000);15  await context.resetChildExpirationTime();16  await page.waitForTimeout(10000);17  await context.resetChildExpirationTime();18  await page.waitForTimeout(10000);19  await context.resetChildExpirationTime();20  await page.waitForTimeout(10000);21  await context.resetChildExpirationTime();22  await page.waitForTimeout(10000);23  await context.resetChildExpirationTime();24  await page.waitForTimeout(10000);25  await context.resetChildExpirationTime();26  await page.waitForTimeout(10000);27  await context.resetChildExpirationTime();28  await page.waitForTimeout(Using AI Code Generation
1const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');2const { chromium } = require('playwright');3(async () => {4  const browser = await chromium.launch();5  const context = await browser.newContext();6  const page = await context.newPage();7  resetChildExpirationTime(page);8  await page.waitForTimeout(1000 * 60 * 60 * 2);9  await page.screenshot({ path: `example.png` });10  await browser.close();11})();12const { chromium } = require('playwright');13(async () => {14  const browser = await chromium.launch();15  const context = await browser.newContext();16  const page = await context.newPage();17  await page.waitForTimeout(1000 * 60 * 60 * 2);18  await page.screenshot({ path: `example.png` });19  await browser.close();20})();21const { chromium } = require('playwright');22(async () => {23  const browser = await chromium.launch();24  const context = await browser.newContext();25  const page = await context.newPage();26  await page.waitForTimeout(1000 * 60 * 60 * 2);27  await page.screenshot({ path: `example.png` });28  await browser.close();29})();30const { chromium } = require('playwright');31(async () => {32  const browser = await chromium.launch();33  const context = await browser.newContext();34  const page = await context.newPage();35  await page.waitForTimeout(1000 * 60 * 60 * 2);36  await page.screenshot({ path: `example.png` });37  await browser.close();38})();39const { chromium } = require('playwright');40(async () => {41  const browser = await chromium.launch();42  const context = await browser.newContext();43  const page = await context.newPage();44  await page.waitForTimeout(1000 * 60 * 60 * 2);45  await page.screenshot({ path: `example.png` });Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch({ headless: false });4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.waitForTimeout(1000);7  await page.resetChildExpirationTime();8  await page.waitForTimeout(1000);9  await browser.close();10})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch({4  });5  const page = await browser.newPage();6  await page.waitForTimeout(10000);7  await page.resetChildExpirationTime();8  await page.waitForTimeout(10000);9})();10const { chromium } = require('playwright');11(async () => {12  const browser = await chromium.launch({13  });14  const context = await browser.newContext();15  const page = await context.newPage();16  await page.waitForTimeout(10000);17  await browser.resetChildExpirationTime();18  await page.waitForTimeout(10000);19})();20const { chromium } = require('playwright');21(async () => {22  const browser = await chromium.launch({23  });24  const context = await browser.newContext();25  const page = await context.newPage();26  const frame = page.mainFrame();27  await page.goto('httpsUsing AI Code Generation
1const playwright = require('playwright');2const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');3const browser = await playwright.chromium.launch();4const context = await browser.newContext();5const expirationTime = 60 * 1000;6resetChildExpirationTime(context, expirationTime);7const page = await context.newPage();8await browser.close();9const playwright = require('playwright');10const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');11const browser = await playwright.chromium.launch();12const context = await browser.newContext();13const expirationTime = 60 * 1000;14resetChildExpirationTime(context, expirationTime);15const page = await context.newPage();16await browser.close();Using AI Code Generation
1const { resetChildExpirationTime } = require('playwright-core/lib/server/browserContext');2const { chromium } = require('playwright-core');3const { chromium } = require('playwright-core');4(async () => {5  const browser = await chromium.launch({ headless: false });6  const context = await browser.newContext();7  await context.newPage();8  await context.newPage();9  await context.newPage();10  await context.newPage();11  resetChildExpirationTime(context);12  await browser.close();13})();14const { resetChildExpirationTime } = require('playwright-core/lib/server/browserContext');15const { chromium } = require('playwright-core');16const { chromium } = require('playwright-core');17(async () => {18  const browser = await chromium.launch({ headless: false });19  const context = await browser.newContext();20  await context.newPage();21  await context.newPage();22  await context.newPage();23  await context.newPage();24  resetChildExpirationTime(context);25  await browser.close();26})();Using AI Code Generation
1const { resetChildExpirationTime } = require('@playwright/test/lib/runner/Test');2resetChildExpirationTime();3const { setChildExpirationTime } = require('@playwright/test/lib/runner/Test');4setChildExpirationTime(1000);5const { setTestTimeout } = require('@playwright/test/lib/runner/Test');6setTestTimeout(1000);7const { setTestName } = require('@playwright/test/lib/runner/Test');8setTestName('testName');9const { setWorkerIndex } = require('@playwright/test/lib/runner/Test');10setWorkerIndex(1);11const { setWorkerOrdinal } = require('@playwright/test/lib/runner/Test');12setWorkerOrdinal(2);13const { setWorkerFixtureValue } = require('@playwright/test/lib/runner/Test');14setWorkerFixtureValue('key', 'value');15const { setConfig } = require('@playwright/test/lib/runner/Test');16setConfig('key', 'value');17const { setFixtures } = require('@playwright/test/lib/runner/Test');18setFixtures({});19const { setTestType } = require('@playwright/test/lib/runner/Test');20setTestType('testType');21const { setTestInfo } = require('@playwright/test/lib/runner/Test');Using AI Code Generation
1const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');2resetChildExpirationTime(browserContext, timeout);3const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');4resetChildExpirationTime(browserContext, timeout);5  ✓ test (2s)6  1 passed (2s)7const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');8resetChildExpirationTime(browserContext, 0);9  1 failed (2s)10      at Object.<anonymous> (test.js:4:3)11const { resetChildExpirationTime } = require('playwright/lib/server/browserContext');12resetChildExpirationTime(browserContext, 1);13  1 failed (2s)Using AI Code Generation
1const { resetChildExpirationTime } = require('playwright/lib/utils/registry');2resetChildExpirationTime(300000);3const playwright = require('playwright');4(async () => {5  for (const browserType of BROWSER) {6    const browser = await playwright[browserType].launch();7    const context = await browser.newContext();8    const page = await context.newPage();9    await page.screenshot({ path: `${browserType}.png` });10    await browser.close();11  }12})();13const { resetChildExpirationTime } = require('playwright/lib/utils/registry');14resetChildExpirationTime(300000);15const playwright = require('playwright');16(async () => {17  for (const browserType of BROWSER) {18    const browser = await playwright[browserType].launch();19    const context = await browser.newContext();20    const page = await context.newPage();21    await page.screenshot({ path: `${browserType}.png` });22    await browser.close();23  }24})();25const { resetChildExpirationTime } = require('playwright/lib/utils/registry');26resetChildExpirationTime(300000);27const playwright = require('playwright');28(async () => {29  for (const browserType of BROWSER) {30    const browser = await playwright[browserType].launch();31    const context = await browser.newContext();32    const page = await context.newPage();33    await page.screenshot({ path: `${browserType}.png` });34    await browser.close();35  }36})();37const { resetChildExpirationTime } = require('playwright/lib/utils/registry');LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!
