packages/react-reconciler/src/ReactFiberCompleteWork.js JAVASCRIPT 2,115 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,115.
1/**2 * Copyright (c) Meta Platforms, Inc. and 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 */910import type {Fiber, FiberRoot} from './ReactInternalTypes';11import type {RootState} from './ReactFiberRoot';12import type {Lanes, Lane} from './ReactFiberLane';13import type {ReactScopeInstance, ReactContext} from 'shared/ReactTypes';14import type {15  Instance,16  Type,17  Props,18  Container,19  ChildSet,20  Resource,21} from './ReactFiberConfig';22import type {ActivityState} from './ReactFiberActivityComponent';23import type {24  SuspenseState,25  SuspenseListRenderState,26  RetryQueue,27} from './ReactFiberSuspenseComponent';28import type {29  OffscreenState,30  OffscreenQueue,31} from './ReactFiberOffscreenComponent';32import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';33import type {Cache} from './ReactFiberCacheComponent';34import {35  enableLegacyHidden,36  enableSuspenseCallback,37  enableScopeAPI,38  enableProfilerTimer,39  enableTransitionTracing,40  passChildrenWhenCloningPersistedNodes,41  disableLegacyMode,42  enableViewTransition,43  enableViewTransitionParentEnterExit,44  enableSuspenseyImages,45} from 'shared/ReactFeatureFlags';4647import {now} from './Scheduler';4849import {50  FunctionComponent,51  ClassComponent,52  HostRoot,53  HostComponent,54  HostHoistable,55  HostSingleton,56  HostText,57  HostPortal,58  ContextProvider,59  ContextConsumer,60  ForwardRef,61  Fragment,62  Mode,63  Profiler,64  SuspenseComponent,65  SuspenseListComponent,66  MemoComponent,67  SimpleMemoComponent,68  LazyComponent,69  IncompleteClassComponent,70  IncompleteFunctionComponent,71  ScopeComponent,72  OffscreenComponent,73  LegacyHiddenComponent,74  CacheComponent,75  TracingMarkerComponent,76  Throw,77  ViewTransitionComponent,78  ActivityComponent,79} from './ReactWorkTags';80import {81  NoMode,82  ConcurrentMode,83  ProfileMode,84  SuspenseyImagesMode,85} from './ReactTypeOfMode';86import {87  Placement,88  Update,89  Visibility,90  NoFlags,91  DidCapture,92  Snapshot,93  ChildDeletion,94  StaticMask,95  Passive,96  ForceClientRender,97  MaySuspendCommit,98  ScheduleRetry,99  ShouldSuspendCommit,100  Cloned,101  ViewTransitionStatic,102  ViewTransitionStaticParent,103  Hydrate,104  PortalStatic,105} from './ReactFiberFlags';106107import {108  createInstance,109  createTextInstance,110  resolveSingletonInstance,111  appendInitialChild,112  finalizeInitialChildren,113  finalizeHydratedChildren,114  supportsMutation,115  supportsPersistence,116  supportsResources,117  supportsSingletons,118  cloneInstance,119  cloneHiddenInstance,120  cloneHiddenTextInstance,121  createContainerChildSet,122  appendChildToContainerChildSet,123  finalizeContainerChildren,124  preparePortalMount,125  prepareScopeUpdate,126  maySuspendCommit,127  maySuspendCommitOnUpdate,128  maySuspendCommitInSyncRender,129  mayResourceSuspendCommit,130  preloadInstance,131  preloadResource,132} from './ReactFiberConfig';133import {134  getRootHostContainer,135  popHostContext,136  getHostContext,137  popHostContainer,138} from './ReactFiberHostContext';139import {140  suspenseStackCursor,141  popSuspenseListContext,142  popSuspenseHandler,143  pushSuspenseListContext,144  pushSuspenseListCatch,145  setShallowSuspenseListContext,146  ForceSuspenseFallback,147  setDefaultShallowSuspenseListContext,148} from './ReactFiberSuspenseContext';149import {popHiddenContext} from './ReactFiberHiddenContext';150import {findFirstSuspended} from './ReactFiberSuspenseComponent';151import {152  isContextProvider as isLegacyContextProvider,153  popContext as popLegacyContext,154  popTopLevelContextObject as popTopLevelLegacyContextObject,155} from './ReactFiberLegacyContext';156import {popProvider} from './ReactFiberNewContext';157import {158  prepareToHydrateHostInstance,159  prepareToHydrateHostTextInstance,160  prepareToHydrateHostActivityInstance,161  prepareToHydrateHostSuspenseInstance,162  popHydrationState,163  resetHydrationState,164  getIsHydrating,165  upgradeHydrationErrorsToRecoverable,166  emitPendingHydrationWarnings,167} from './ReactFiberHydrationContext';168import {169  renderHasNotSuspendedYet,170  getRenderTargetTime,171  getWorkInProgressTransitions,172  shouldRemainOnPreviousScreen,173  markSpawnedRetryLane,174} from './ReactFiberWorkLoop';175import {176  OffscreenLane,177  SomeRetryLane,178  NoLanes,179  includesSomeLane,180  mergeLanes,181  claimNextRetryLane,182  includesOnlySuspenseyCommitEligibleLanes,183} from './ReactFiberLane';184import {resetChildFibers} from './ReactChildFiber';185import {createScopeInstance} from './ReactFiberScope';186import {transferActualDuration} from './ReactProfilerTimer';187import {popCacheProvider} from './ReactFiberCacheComponent';188import {popTreeContext, pushTreeFork} from './ReactFiberTreeContext';189import {popRootTransition, popTransition} from './ReactFiberTransition';190import {191  popMarkerInstance,192  popRootMarkerInstance,193} from './ReactFiberTracingMarkerComponent';194import {suspendCommit} from './ReactFiberThenable';195import type {Flags} from './ReactFiberFlags';196197/**198 * Tag the fiber with an update effect. This turns a Placement into199 * a PlacementAndUpdate.200 */201function markUpdate(workInProgress: Fiber) {202  workInProgress.flags |= Update;203}204205/**206 * Tag the fiber with Cloned in persistent mode to signal that207 * it received an update that requires a clone of the tree above.208 */209function markCloned(workInProgress: Fiber) {210  // $FlowFixMe[constant-condition]211  if (supportsPersistence) {212    workInProgress.flags |= Cloned;213  }214}215216/**217 * In persistent mode, return whether this update needs to clone the subtree.218 */219function doesRequireClone(current: null | Fiber, completedWork: Fiber) {220  const didBailout = current !== null && current.child === completedWork.child;221  if (didBailout) {222    return false;223  }224225  if ((completedWork.flags & ChildDeletion) !== NoFlags) {226    return true;227  }228229  // TODO: If we move the `doesRequireClone` call after `bubbleProperties`230  // then we only have to check the `completedWork.subtreeFlags`.231  let child = completedWork.child;232  while (child !== null) {233    const checkedFlags = Cloned | Visibility | Placement | ChildDeletion;234    if (235      (child.flags & checkedFlags) !== NoFlags ||236      (child.subtreeFlags & checkedFlags) !== NoFlags237    ) {238      return true;239    }240    child = child.sibling;241  }242  return false;243}244245function appendAllChildren(246  parent: Instance,247  workInProgress: Fiber,248  needsVisibilityToggle: boolean,249  isHidden: boolean,250) {251  // $FlowFixMe[constant-condition]252  if (supportsMutation) {253    // We only have the top Fiber that was created but we need recurse down its254    // children to find all the terminal nodes.255    let node = workInProgress.child;256    while (node !== null) {257      if (node.tag === HostComponent || node.tag === HostText) {258        appendInitialChild(parent, node.stateNode);259      } else if (260        node.tag === HostPortal ||261        // $FlowFixMe[constant-condition]262        (supportsSingletons ? node.tag === HostSingleton : false)263      ) {264        // If we have a portal child, then we don't want to traverse265        // down its children. Instead, we'll get insertions from each child in266        // the portal directly.267        // If we have a HostSingleton it will be placed independently268      } else if (node.child !== null) {269        node.child.return = node;270        node = node.child;271        continue;272      }273      if (node === workInProgress) {274        return;275      }276      // $FlowFixMe[incompatible-use] found when upgrading Flow277      while (node.sibling === null) {278        // $FlowFixMe[incompatible-use] found when upgrading Flow279        if (node.return === null || node.return === workInProgress) {280          return;281        }282        node = node.return;283      }284      // $FlowFixMe[incompatible-use] found when upgrading Flow285      node.sibling.return = node.return;286      node = node.sibling;287    }288    // $FlowFixMe[constant-condition]289  } else if (supportsPersistence) {290    // We only have the top Fiber that was created but we need recurse down its291    // children to find all the terminal nodes.292    let node = workInProgress.child;293    while (node !== null) {294      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);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);309        }310        appendInitialChild(parent, instance);311      } else if (node.tag === HostPortal) {312        // If we have a portal child, then we don't want to traverse313        // down its children. Instead, we'll get insertions from each child in314        // the portal directly.315      } else if (316        node.tag === OffscreenComponent &&317        node.memoizedState !== null318      ) {319        // The children in this boundary are hidden. Toggle their visibility320        // before appending.321        const child = node.child;322        if (child !== null) {323          child.return = node;324        }325        appendAllChildren(326          parent,327          node,328          /* needsVisibilityToggle */ true,329          /* isHidden */ true,330        );331      } else if (node.child !== null) {332        node.child.return = node;333        node = node.child;334        continue;335      }336      if (node === workInProgress) {337        return;338      }339      // $FlowFixMe[incompatible-use] found when upgrading Flow340      while (node.sibling === null) {341        // $FlowFixMe[incompatible-use] found when upgrading Flow342        if (node.return === null || node.return === workInProgress) {343          return;344        }345        node = node.return;346      }347      // $FlowFixMe[incompatible-use] found when upgrading Flow348      node.sibling.return = node.return;349      node = node.sibling;350    }351  }352}353354// An unfortunate fork of appendAllChildren because we have two different parent types.355function appendAllChildrenToContainer(356  containerChildSet: ChildSet,357  workInProgress: Fiber,358  needsVisibilityToggle: boolean,359  isHidden: boolean,360): boolean {361  // Host components that have their visibility toggled by an OffscreenComponent362  // do not support passChildrenWhenCloningPersistedNodes. To inform the callee363  // about their presence, we track and return if they were added to the364  // child set.365  let hasOffscreenComponentChild = false;366  // $FlowFixMe[constant-condition]367  if (supportsPersistence) {368    // We only have the top Fiber that was created but we need recurse down its369    // children to find all the terminal nodes.370    let node = workInProgress.child;371    while (node !== null) {372      if (node.tag === HostComponent) {373        let instance = node.stateNode;374        if (needsVisibilityToggle && isHidden) {375          // This child is inside a timed out tree. Hide it.376          const props = node.memoizedProps;377          const type = node.type;378          instance = cloneHiddenInstance(instance, type, props);379        }380        appendChildToContainerChildSet(containerChildSet, instance);381      } else if (node.tag === HostText) {382        let instance = node.stateNode;383        if (needsVisibilityToggle && isHidden) {384          // This child is inside a timed out tree. Hide it.385          const text = node.memoizedProps;386          instance = cloneHiddenTextInstance(instance, text);387        }388        appendChildToContainerChildSet(containerChildSet, instance);389      } else if (node.tag === HostPortal) {390        // If we have a portal child, then we don't want to traverse391        // down its children. Instead, we'll get insertions from each child in392        // the portal directly.393      } else if (394        node.tag === OffscreenComponent &&395        node.memoizedState !== null396      ) {397        // The children in this boundary are hidden. Toggle their visibility398        // before appending.399        const child = node.child;400        if (child !== null) {401          child.return = node;402        }403        appendAllChildrenToContainer(404          containerChildSet,405          node,406          /* needsVisibilityToggle */ true,407          /* isHidden */ true,408        );409410        hasOffscreenComponentChild = true;411      } else if (node.child !== null) {412        node.child.return = node;413        node = node.child;414        continue;415      }416      node = node as Fiber;417      if (node === workInProgress) {418        return hasOffscreenComponentChild;419      }420      // $FlowFixMe[incompatible-use] found when upgrading Flow421      while (node.sibling === null) {422        // $FlowFixMe[incompatible-use] found when upgrading Flow423        if (node.return === null || node.return === workInProgress) {424          return hasOffscreenComponentChild;425        }426        node = node.return;427      }428      // $FlowFixMe[incompatible-use] found when upgrading Flow429      node.sibling.return = node.return;430      node = node.sibling;431    }432  }433434  return hasOffscreenComponentChild;435}436437function updateHostContainer(current: null | Fiber, workInProgress: Fiber) {438  // $FlowFixMe[constant-condition]439  if (supportsPersistence) {440    if (doesRequireClone(current, workInProgress)) {441      const portalOrRoot: {442        containerInfo: Container,443        pendingChildren: ChildSet,444        ...445      } = workInProgress.stateNode;446      const container = portalOrRoot.containerInfo;447      const newChildSet = createContainerChildSet();448      // If children might have changed, we have to add them all to the set.449      appendAllChildrenToContainer(450        newChildSet,451        workInProgress,452        /* needsVisibilityToggle */ false,453        /* isHidden */ false,454      );455      portalOrRoot.pendingChildren = newChildSet;456      // Schedule an update on the container to swap out the container.457      markUpdate(workInProgress);458      finalizeContainerChildren(container, newChildSet);459    }460  }461}462463function updateHostComponent(464  current: Fiber,465  workInProgress: Fiber,466  type: Type,467  newProps: Props,468  renderLanes: Lanes,469) {470  // $FlowFixMe[constant-condition]471  if (supportsMutation) {472    // If we have an alternate, that means this is an update and we need to473    // schedule a side-effect to do the updates.474    const oldProps = current.memoizedProps;475    if (oldProps === newProps) {476      // In mutation mode, this is sufficient for a bailout because477      // we won't touch this node even if children changed.478      return;479    }480481    markUpdate(workInProgress);482    // $FlowFixMe[constant-condition]483  } else if (supportsPersistence) {484    const currentInstance = current.stateNode;485    const oldProps = current.memoizedProps;486    // If there are no effects associated with this node, then none of our children had any updates.487    // This guarantees that we can reuse all of them.488    const requiresClone = doesRequireClone(current, workInProgress);489    if (!requiresClone && oldProps === newProps) {490      // No changes, just reuse the existing instance.491      // Note that this might release a previous clone.492      workInProgress.stateNode = currentInstance;493      return;494    }495    const currentHostContext = getHostContext();496497    let newChildSet = null;498    let hasOffscreenComponentChild = false;499    if (requiresClone && passChildrenWhenCloningPersistedNodes) {500      markCloned(workInProgress);501      newChildSet = createContainerChildSet();502      // If children might have changed, we have to add them all to the set.503      hasOffscreenComponentChild = appendAllChildrenToContainer(504        newChildSet,505        workInProgress,506        /* needsVisibilityToggle */ false,507        /* isHidden */ false,508      );509    }510511    const newInstance = cloneInstance(512      currentInstance,513      type,514      oldProps,515      newProps,516      !requiresClone,517      !hasOffscreenComponentChild ? newChildSet : undefined,518    );519    if (newInstance === currentInstance) {520      // No changes, just reuse the existing instance.521      // Note that this might release a previous clone.522      workInProgress.stateNode = currentInstance;523      return;524    } else {525      markCloned(workInProgress);526    }527528    // Certain renderers require commit-time effects for initial mount.529    // (eg DOM renderer supports auto-focus for certain elements).530    // Make sure such renderers get scheduled for later work.531    if (532      finalizeInitialChildren(newInstance, type, newProps, currentHostContext)533    ) {534      markUpdate(workInProgress);535    }536    workInProgress.stateNode = newInstance;537    if (538      requiresClone &&539      (!passChildrenWhenCloningPersistedNodes || hasOffscreenComponentChild)540    ) {541      // If children have changed, we have to add them all to the set.542      appendAllChildren(543        newInstance,544        workInProgress,545        /* needsVisibilityToggle */ false,546        /* isHidden */ false,547      );548    }549  }550}551552// This function must be called at the very end of the complete phase, because553// it might throw to suspend, and if the resource immediately loads, the work554// loop will resume rendering as if the work-in-progress completed. So it must555// fully complete.556// TODO: This should ideally move to begin phase, but currently the instance is557// not created until the complete phase. For our existing use cases, host nodes558// that suspend don't have children, so it doesn't matter. But that might not559// always be true in the future.560function preloadInstanceAndSuspendIfNeeded(561  workInProgress: Fiber,562  type: Type,563  oldProps: null | Props,564  newProps: Props,565  renderLanes: Lanes,566) {567  const maySuspend =568    (enableSuspenseyImages ||569      (workInProgress.mode & SuspenseyImagesMode) !== NoMode) &&570    (oldProps === null571      ? maySuspendCommit(type, newProps)572      : maySuspendCommitOnUpdate(type, oldProps, newProps));573574  if (!maySuspend) {575    // If this flag was set previously, we can remove it. The flag576    // represents whether this particular set of props might ever need to577    // suspend. The safest thing to do is for maySuspendCommit to always578    // return true, but if the renderer is reasonably confident that the579    // underlying resource won't be evicted, it can return false as a580    // performance optimization.581    workInProgress.flags &= ~MaySuspendCommit;582    return;583  }584585  // Mark this fiber with a flag. This gets set on all host instances586  // that might possibly suspend, even if they don't need to suspend587  // currently. We use this when revealing a prerendered tree, because588  // even though the tree has "mounted", its resources might not have589  // loaded yet.590  workInProgress.flags |= MaySuspendCommit;591592  if (593    includesOnlySuspenseyCommitEligibleLanes(renderLanes) ||594    maySuspendCommitInSyncRender(type, newProps)595  ) {596    // preload the instance if necessary. Even if this is an urgent render there597    // could be benefits to preloading early.598    // @TODO we should probably do the preload in begin work599    const isReady = preloadInstance(workInProgress.stateNode, type, newProps);600    if (!isReady) {601      if (shouldRemainOnPreviousScreen()) {602        workInProgress.flags |= ShouldSuspendCommit;603      } else {604        suspendCommit();605      }606    } else {607      // Even if we're ready we suspend the commit and check again in the pre-commit608      // phase if we need to suspend anyway. Such as if it's delayed on decoding or609      // if it was dropped from the cache while rendering due to pressure.610      workInProgress.flags |= ShouldSuspendCommit;611    }612  }613}614615function preloadResourceAndSuspendIfNeeded(616  workInProgress: Fiber,617  resource: Resource,618  type: Type,619  props: Props,620  renderLanes: Lanes,621) {622  // This is a fork of preloadInstanceAndSuspendIfNeeded, but for resources.623  if (!mayResourceSuspendCommit(resource)) {624    workInProgress.flags &= ~MaySuspendCommit;625    return;626  }627628  workInProgress.flags |= MaySuspendCommit;629630  const isReady = preloadResource(resource);631  if (!isReady) {632    if (shouldRemainOnPreviousScreen()) {633      workInProgress.flags |= ShouldSuspendCommit;634    } else {635      suspendCommit();636    }637  }638}639640function scheduleRetryEffect(641  workInProgress: Fiber,642  retryQueue: RetryQueue | null,643) {644  const wakeables = retryQueue;645  if (wakeables !== null) {646    // Schedule an effect to attach a retry listener to the promise.647    // TODO: Move to passive phase648    workInProgress.flags |= Update;649  }650651  // Check if we need to schedule an immediate retry. This should happen652  // whenever we unwind a suspended tree without fully rendering its siblings;653  // we need to begin the retry so we can start prerendering them.654  //655  // We also use this mechanism for Suspensey Resources (e.g. stylesheets),656  // because those don't actually block the render phase, only the commit phase.657  // So we can start rendering even before the resources are ready.658  if (workInProgress.flags & ScheduleRetry) {659    const retryLane =660      // TODO: This check should probably be moved into claimNextRetryLane661      // I also suspect that we need some further consolidation of offscreen662      // and retry lanes.663      workInProgress.tag !== OffscreenComponent664        ? claimNextRetryLane()665        : OffscreenLane;666    workInProgress.lanes = mergeLanes(workInProgress.lanes, retryLane);667668    // Track the lanes that have been scheduled for an immediate retry so that669    // we can mark them as suspended upon committing the root.670    markSpawnedRetryLane(retryLane);671  }672}673674function updateHostText(675  current: Fiber,676  workInProgress: Fiber,677  oldText: string,678  newText: string,679) {680  // $FlowFixMe[constant-condition]681  if (supportsMutation) {682    // If the text differs, mark it as an update. All the work in done in commitWork.683    if (oldText !== newText) {684      markUpdate(workInProgress);685    }686    // $FlowFixMe[constant-condition]687  } else if (supportsPersistence) {688    if (oldText !== newText) {689      // If the text content differs, we'll create a new text instance for it.690      const rootContainerInstance = getRootHostContainer();691      const currentHostContext = getHostContext();692      markCloned(workInProgress);693      workInProgress.stateNode = createTextInstance(694        newText,695        rootContainerInstance,696        currentHostContext,697        workInProgress,698      );699    } else {700      workInProgress.stateNode = current.stateNode;701    }702  }703}704705function cutOffTailIfNeeded(706  renderState: SuspenseListRenderState,707  hasRenderedATailFallback: boolean,708) {709  if (getIsHydrating()) {710    // If we're hydrating, we should consume as many items as we can711    // so we don't leave any behind.712    return;713  }714  switch (renderState.tailMode) {715    case 'visible': {716      // Everything should remain as it was.717      break;718    }719    case 'collapsed': {720      // Any insertions at the end of the tail list after this point721      // should be invisible. If there are already mounted boundaries722      // anything before them are not considered for collapsing.723      // Therefore we need to go through the whole tail to find if724      // there are any.725      let tailNode = renderState.tail;726      let lastTailNode = null;727      while (tailNode !== null) {728        if (tailNode.alternate !== null) {729          lastTailNode = tailNode;730        }731        tailNode = tailNode.sibling;732      }733      // Next we're simply going to delete all insertions after the734      // last rendered item.735      if (lastTailNode === null) {736        // All remaining items in the tail are insertions.737        if (!hasRenderedATailFallback && renderState.tail !== null) {738          // We suspended during the head. We want to show at least one739          // row at the tail. So we'll keep on and cut off the rest.740          renderState.tail.sibling = null;741        } else {742          renderState.tail = null;743        }744      } else {745        // Detach the insertion after the last node that was already746        // inserted.747        lastTailNode.sibling = null;748      }749      break;750    }751    // Hidden is now the default.752    case 'hidden':753    default: {754      // Any insertions at the end of the tail list after this point755      // should be invisible. If there are already mounted boundaries756      // anything before them are not considered for collapsing.757      // Therefore we need to go through the whole tail to find if758      // there are any.759      let tailNode = renderState.tail;760      let lastTailNode = null;761      while (tailNode !== null) {762        if (tailNode.alternate !== null) {763          lastTailNode = tailNode;764        }765        tailNode = tailNode.sibling;766      }767      // Next we're simply going to delete all insertions after the768      // last rendered item.769      if (lastTailNode === null) {770        // All remaining items in the tail are insertions.771        renderState.tail = null;772      } else {773        // Detach the insertion after the last node that was already774        // inserted.775        lastTailNode.sibling = null;776      }777      break;778    }779  }780}781782function isOnlyNewMounts(tail: Fiber): boolean {783  let fiber: null | Fiber = tail;784  while (fiber !== null) {785    if (fiber.alternate !== null) {786      return false;787    }788    fiber = fiber.sibling;789  }790  return true;791}792793function bubbleProperties(completedWork: Fiber) {794  const didBailout =795    completedWork.alternate !== null &&796    completedWork.alternate.child === completedWork.child;797798  let newChildLanes: Lanes = NoLanes;799  let subtreeFlags: Flags = NoFlags;800801  if (!didBailout) {802    // Bubble up the earliest expiration time.803    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {804      // In profiling mode, resetChildExpirationTime is also used to reset805      // profiler durations.806      let actualDuration = completedWork.actualDuration;807      let treeBaseDuration = completedWork.selfBaseDuration as any as number;808809      let child = completedWork.child;810      while (child !== null) {811        newChildLanes = mergeLanes(812          newChildLanes,813          mergeLanes(child.lanes, child.childLanes),814        );815816        subtreeFlags |= child.subtreeFlags;817        subtreeFlags |= child.flags;818819        // When a fiber is cloned, its actualDuration is reset to 0. This value will820        // only be updated if work is done on the fiber (i.e. it doesn't bailout).821        // When work is done, it should bubble to the parent's actualDuration. If822        // the fiber has not been cloned though, (meaning no work was done), then823        // this value will reflect the amount of time spent working on a previous824        // render. In that case it should not bubble. We determine whether it was825        // cloned by comparing the child pointer.826        // $FlowFixMe[unsafe-addition] addition with possible null/undefined value827        actualDuration += child.actualDuration;828829        // $FlowFixMe[unsafe-addition] addition with possible null/undefined value830        treeBaseDuration += child.treeBaseDuration;831        child = child.sibling;832      }833834      completedWork.actualDuration = actualDuration;835      completedWork.treeBaseDuration = treeBaseDuration;836    } else {837      let child = completedWork.child;838      while (child !== null) {839        newChildLanes = mergeLanes(840          newChildLanes,841          mergeLanes(child.lanes, child.childLanes),842        );843844        subtreeFlags |= child.subtreeFlags;845        subtreeFlags |= child.flags;846847        // Update the return pointer so the tree is consistent. This is a code848        // smell because it assumes the commit phase is never concurrent with849        // the render phase. Will address during refactor to alternate model.850        child.return = completedWork;851852        child = child.sibling;853      }854    }855856    completedWork.subtreeFlags |= subtreeFlags;857  } else {858    // Bubble up the earliest expiration time.859    if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {860      // In profiling mode, resetChildExpirationTime is also used to reset861      // profiler durations.862      let treeBaseDuration = completedWork.selfBaseDuration as any as number;863864      let child = completedWork.child;865      while (child !== null) {866        newChildLanes = mergeLanes(867          newChildLanes,868          mergeLanes(child.lanes, child.childLanes),869        );870871        // "Static" flags share the lifetime of the fiber/hook they belong to,872        // so we should bubble those up even during a bailout. All the other873        // flags have a lifetime only of a single render + commit, so we should874        // ignore them.875        subtreeFlags |= child.subtreeFlags & StaticMask;876        subtreeFlags |= child.flags & StaticMask;877878        // $FlowFixMe[unsafe-addition] addition with possible null/undefined value879        treeBaseDuration += child.treeBaseDuration;880        child = child.sibling;881      }882883      completedWork.treeBaseDuration = treeBaseDuration;884    } else {885      let child = completedWork.child;886      while (child !== null) {887        newChildLanes = mergeLanes(888          newChildLanes,889          mergeLanes(child.lanes, child.childLanes),890        );891892        // "Static" flags share the lifetime of the fiber/hook they belong to,893        // so we should bubble those up even during a bailout. All the other894        // flags have a lifetime only of a single render + commit, so we should895        // ignore them.896        subtreeFlags |= child.subtreeFlags & StaticMask;897        subtreeFlags |= child.flags & StaticMask;898899        // Update the return pointer so the tree is consistent. This is a code900        // smell because it assumes the commit phase is never concurrent with901        // the render phase. Will address during refactor to alternate model.902        child.return = completedWork;903904        child = child.sibling;905      }906    }907908    completedWork.subtreeFlags |= subtreeFlags;909  }910911  completedWork.childLanes = newChildLanes;912913  return didBailout;914}915916function completeDehydratedActivityBoundary(917  current: Fiber | null,918  workInProgress: Fiber,919  nextState: ActivityState | null,920): boolean {921  const wasHydrated = popHydrationState(workInProgress);922923  if (nextState !== null) {924    // We might be inside a hydration state the first time we're picking up this925    // Activity boundary, and also after we've reentered it for further hydration.926    if (current === null) {927      if (!wasHydrated) {928        throw new Error(929          'A dehydrated suspense component was completed without a hydrated node. ' +930            'This is probably a bug in React.',931        );932      }933      prepareToHydrateHostActivityInstance(workInProgress);934      bubbleProperties(workInProgress);935      if (enableProfilerTimer) {936        if ((workInProgress.mode & ProfileMode) !== NoMode) {937          // $FlowFixMe[invalid-compare]938          const isTimedOutSuspense = nextState !== null;939          if (isTimedOutSuspense) {940            // Don't count time spent in a timed out Suspense subtree as part of the base duration.941            const primaryChildFragment = workInProgress.child;942            if (primaryChildFragment !== null) {943              // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator944              workInProgress.treeBaseDuration -=945                primaryChildFragment.treeBaseDuration as any as number;946            }947          }948        }949      }950      return false;951    } else {952      emitPendingHydrationWarnings();953      // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration954      // state since we're now exiting out of it. popHydrationState doesn't do that for us.955      resetHydrationState();956      if ((workInProgress.flags & DidCapture) === NoFlags) {957        // This boundary did not suspend so it's now hydrated and unsuspended.958        nextState = workInProgress.memoizedState = null;959      }960      // If nothing suspended, we need to schedule an effect to mark this boundary961      // as having hydrated so events know that they're free to be invoked.962      // It's also a signal to replay events and the suspense callback.963      // If something suspended, schedule an effect to attach retry listeners.964      // So we might as well always mark this.965      workInProgress.flags |= Update;966      bubbleProperties(workInProgress);967      if (enableProfilerTimer) {968        if ((workInProgress.mode & ProfileMode) !== NoMode) {969          const isTimedOutSuspense = nextState !== null;970          if (isTimedOutSuspense) {971            // Don't count time spent in a timed out Suspense subtree as part of the base duration.972            const primaryChildFragment = workInProgress.child;973            if (primaryChildFragment !== null) {974              // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator975              workInProgress.treeBaseDuration -=976                primaryChildFragment.treeBaseDuration as any as number;977            }978          }979        }980      }981      return false;982    }983  } else {984    // Successfully completed this tree. If this was a forced client render,985    // there may have been recoverable errors during first hydration986    // attempt. If so, add them to a queue so we can log them in the987    // commit phase. We also add them to prev state so we can get to them988    // from the Suspense Boundary.989    const hydrationErrors = upgradeHydrationErrorsToRecoverable();990    if (current !== null && current.memoizedState !== null) {991      const prevState: ActivityState = current.memoizedState;992      prevState.hydrationErrors = hydrationErrors;993    }994    // Fall through to normal Offscreen path995    return true;996  }997}998999function completeDehydratedSuspenseBoundary(1000  current: Fiber | null,1001  workInProgress: Fiber,1002  nextState: SuspenseState | null,1003): boolean {1004  const wasHydrated = popHydrationState(workInProgress);10051006  if (nextState !== null && nextState.dehydrated !== null) {1007    // We might be inside a hydration state the first time we're picking up this1008    // Suspense boundary, and also after we've reentered it for further hydration.1009    if (current === null) {1010      if (!wasHydrated) {1011        throw new Error(1012          'A dehydrated suspense component was completed without a hydrated node. ' +1013            'This is probably a bug in React.',1014        );1015      }1016      prepareToHydrateHostSuspenseInstance(workInProgress);1017      bubbleProperties(workInProgress);1018      if (enableProfilerTimer) {1019        if ((workInProgress.mode & ProfileMode) !== NoMode) {1020          // $FlowFixMe[invalid-compare]1021          const isTimedOutSuspense = nextState !== null;1022          if (isTimedOutSuspense) {1023            // Don't count time spent in a timed out Suspense subtree as part of the base duration.1024            const primaryChildFragment = workInProgress.child;1025            if (primaryChildFragment !== null) {1026              // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator1027              workInProgress.treeBaseDuration -=1028                primaryChildFragment.treeBaseDuration as any as number;1029            }1030          }1031        }1032      }1033      return false;1034    } else {1035      emitPendingHydrationWarnings();1036      // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration1037      // state since we're now exiting out of it. popHydrationState doesn't do that for us.1038      resetHydrationState();1039      if ((workInProgress.flags & DidCapture) === NoFlags) {1040        // This boundary did not suspend so it's now hydrated and unsuspended.1041        nextState = workInProgress.memoizedState = null;1042      }1043      // If nothing suspended, we need to schedule an effect to mark this boundary1044      // as having hydrated so events know that they're free to be invoked.1045      // It's also a signal to replay events and the suspense callback.1046      // If something suspended, schedule an effect to attach retry listeners.1047      // So we might as well always mark this.1048      workInProgress.flags |= Update;1049      bubbleProperties(workInProgress);1050      if (enableProfilerTimer) {1051        if ((workInProgress.mode & ProfileMode) !== NoMode) {1052          const isTimedOutSuspense = nextState !== null;1053          if (isTimedOutSuspense) {1054            // Don't count time spent in a timed out Suspense subtree as part of the base duration.1055            const primaryChildFragment = workInProgress.child;1056            if (primaryChildFragment !== null) {1057              // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator1058              workInProgress.treeBaseDuration -=1059                primaryChildFragment.treeBaseDuration as any as number;1060            }1061          }1062        }1063      }1064      return false;1065    }1066  } else {1067    // Successfully completed this tree. If this was a forced client render,1068    // there may have been recoverable errors during first hydration1069    // attempt. If so, add them to a queue so we can log them in the1070    // commit phase. We also add them to prev state so we can get to them1071    // from the Suspense Boundary.1072    const hydrationErrors = upgradeHydrationErrorsToRecoverable();1073    if (current !== null && current.memoizedState !== null) {1074      const prevState: SuspenseState = current.memoizedState;1075      prevState.hydrationErrors = hydrationErrors;1076    }1077    // Fall through to normal Suspense path1078    return true;1079  }1080}10811082function completeWork(1083  current: Fiber | null,1084  workInProgress: Fiber,1085  renderLanes: Lanes,1086): Fiber | null {1087  const newProps = workInProgress.pendingProps;1088  // Note: This intentionally doesn't check if we're hydrating because comparing1089  // to the current tree provider fiber is just as fast and less error-prone.1090  // Ideally we would have a special version of the work loop only1091  // for hydration.1092  popTreeContext(workInProgress);1093  switch (workInProgress.tag) {1094    case IncompleteFunctionComponent: {1095      if (disableLegacyMode) {1096        break;1097      }1098      // Fallthrough1099    }1100    case LazyComponent:1101    case SimpleMemoComponent:1102    case FunctionComponent:1103    case ForwardRef:1104    case Fragment:1105    case Mode:1106    case Profiler:1107    case ContextConsumer:1108    case MemoComponent:1109      bubbleProperties(workInProgress);1110      return null;1111    case ClassComponent: {1112      const Component = workInProgress.type;1113      if (isLegacyContextProvider(Component)) {1114        popLegacyContext(workInProgress);1115      }1116      bubbleProperties(workInProgress);1117      return null;1118    }1119    case HostRoot: {1120      const fiberRoot = workInProgress.stateNode as FiberRoot;11211122      if (enableTransitionTracing) {1123        const transitions = getWorkInProgressTransitions();1124        // We set the Passive flag here because if there are new transitions,1125        // we will need to schedule callbacks and process the transitions,1126        // which we do in the passive phase1127        if (transitions !== null) {1128          workInProgress.flags |= Passive;1129        }1130      }11311132      let previousCache: Cache | null = null;1133      if (current !== null) {1134        previousCache = current.memoizedState.cache;1135      }1136      const cache: Cache = workInProgress.memoizedState.cache;1137      if (cache !== previousCache) {1138        // Run passive effects to retain/release the cache.1139        workInProgress.flags |= Passive;1140      }1141      popCacheProvider(workInProgress, cache);11421143      if (enableTransitionTracing) {1144        popRootMarkerInstance(workInProgress);1145      }11461147      popRootTransition(workInProgress, fiberRoot, renderLanes);1148      popHostContainer(workInProgress);1149      popTopLevelLegacyContextObject(workInProgress);1150      if (fiberRoot.pendingContext) {1151        fiberRoot.context = fiberRoot.pendingContext;1152        fiberRoot.pendingContext = null;1153      }1154      if (current === null || current.child === null) {1155        // If we hydrated, pop so that we can delete any remaining children1156        // that weren't hydrated.1157        const wasHydrated = popHydrationState(workInProgress);1158        if (wasHydrated) {1159          emitPendingHydrationWarnings();1160          // If we hydrated, then we'll need to schedule an update for1161          // the commit side-effects on the root.1162          markUpdate(workInProgress);1163        } else {1164          if (current !== null) {1165            const prevState: RootState = current.memoizedState;1166            if (1167              // Check if this is a client root1168              !prevState.isDehydrated ||1169              // Check if we reverted to client rendering (e.g. due to an error)1170              (workInProgress.flags & ForceClientRender) !== NoFlags1171            ) {1172              // Schedule an effect to clear this container at the start of the1173              // next commit. This handles the case of React rendering into a1174              // container with previous children. It's also safe to do for1175              // updates too, because current.child would only be null if the1176              // previous render was null (so the container would already1177              // be empty).1178              workInProgress.flags |= Snapshot;11791180              // If this was a forced client render, there may have been1181              // recoverable errors during first hydration attempt. If so, add1182              // them to a queue so we can log them in the commit phase.1183              upgradeHydrationErrorsToRecoverable();1184            }1185          }1186        }1187      }1188      updateHostContainer(current, workInProgress);1189      bubbleProperties(workInProgress);1190      if (enableTransitionTracing) {1191        if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {1192          // If any of our suspense children toggle visibility, this means that1193          // the pending boundaries array needs to be updated, which we only1194          // do in the passive phase.1195          workInProgress.flags |= Passive;1196        }1197      }1198      return null;1199    }1200    case HostHoistable: {1201      // $FlowFixMe[constant-condition]1202      if (supportsResources) {1203        // The branching here is more complicated than you might expect because1204        // a HostHoistable sometimes corresponds to a Resource and sometimes1205        // corresponds to an Instance. It can also switch during an update.12061207        const type = workInProgress.type;1208        const nextResource: Resource | null = workInProgress.memoizedState;1209        if (current === null) {1210          // We are mounting and must Update this Hoistable in this commit1211          // @TODO refactor this block to create the instance here in complete1212          // phase if we are not hydrating.1213          markUpdate(workInProgress);1214          if (nextResource !== null) {1215            // This is a Hoistable Resource12161217            // This must come at the very end of the complete phase.1218            bubbleProperties(workInProgress);1219            preloadResourceAndSuspendIfNeeded(1220              workInProgress,1221              nextResource,1222              type,1223              newProps,1224              renderLanes,1225            );1226            return null;1227          } else {1228            // This is a Hoistable Instance1229            // This must come at the very end of the complete phase.1230            bubbleProperties(workInProgress);1231            preloadInstanceAndSuspendIfNeeded(1232              workInProgress,1233              type,1234              null,1235              newProps,1236              renderLanes,1237            );1238            return null;1239          }1240        } else {1241          // This is an update.1242          if (nextResource) {1243            // This is a Resource1244            if (nextResource !== current.memoizedState) {1245              // we have a new Resource. we need to update1246              markUpdate(workInProgress);1247              // This must come at the very end of the complete phase.1248              bubbleProperties(workInProgress);1249              // This must come at the very end of the complete phase, because it might1250              // throw to suspend, and if the resource immediately loads, the work loop1251              // will resume rendering as if the work-in-progress completed. So it must1252              // fully complete.1253              preloadResourceAndSuspendIfNeeded(1254                workInProgress,1255                nextResource,1256                type,1257                newProps,1258                renderLanes,1259              );1260              return null;1261            } else {1262              // This must come at the very end of the complete phase.1263              bubbleProperties(workInProgress);1264              workInProgress.flags &= ~MaySuspendCommit;1265              return null;1266            }1267          } else {1268            const oldProps = current.memoizedProps;1269            // This is an Instance1270            // We may have props to update on the Hoistable instance.1271            // $FlowFixMe[constant-condition]1272            if (supportsMutation) {1273              if (oldProps !== newProps) {1274                markUpdate(workInProgress);1275              }1276            } else {1277              // We use the updateHostComponent path because it produces1278              // the update queue we need for Hoistables.1279              updateHostComponent(1280                current,1281                workInProgress,1282                type,1283                newProps,1284                renderLanes,1285              );1286            }1287            // This must come at the very end of the complete phase.1288            bubbleProperties(workInProgress);1289            preloadInstanceAndSuspendIfNeeded(1290              workInProgress,1291              type,1292              oldProps,1293              newProps,1294              renderLanes,1295            );1296            return null;1297          }1298        }1299      }1300      // Fall through1301    }1302    case HostSingleton: {1303      // $FlowFixMe[constant-condition]1304      if (supportsSingletons) {1305        popHostContext(workInProgress);1306        const rootContainerInstance = getRootHostContainer();1307        const type = workInProgress.type;1308        if (current !== null && workInProgress.stateNode != null) {1309          // $FlowFixMe[constant-condition]1310          if (supportsMutation) {1311            const oldProps = current.memoizedProps;1312            if (oldProps !== newProps) {1313              markUpdate(workInProgress);1314            }1315          } else {1316            updateHostComponent(1317              current,1318              workInProgress,1319              type,1320              newProps,1321              renderLanes,1322            );1323          }1324        } else {1325          if (!newProps) {1326            if (workInProgress.stateNode === null) {1327              throw new Error(1328                'We must have new props for new mounts. This error is likely ' +1329                  'caused by a bug in React. Please file an issue.',1330              );1331            }13321333            // This can happen when we abort work.1334            bubbleProperties(workInProgress);1335            if (enableViewTransition) {1336              // Host Components act as their own View Transitions which doesn't run enter/exit animations.1337              // We clear any ViewTransitionStatic flag bubbled from inner View Transitions.1338              workInProgress.subtreeFlags &= ~ViewTransitionStatic;1339            }1340            return null;1341          }13421343          const currentHostContext = getHostContext();1344          const wasHydrated = popHydrationState(workInProgress);1345          let instance: Instance;1346          if (wasHydrated) {1347            // We ignore the boolean indicating there is an updateQueue because1348            // it is used only to set text children and HostSingletons do not1349            // use them.1350            prepareToHydrateHostInstance(workInProgress, currentHostContext);1351            instance = workInProgress.stateNode;1352          } else {1353            instance = resolveSingletonInstance(1354              type,1355              newProps,1356              rootContainerInstance,1357              currentHostContext,1358              true,1359            );1360            workInProgress.stateNode = instance;1361            markUpdate(workInProgress);1362          }1363        }1364        bubbleProperties(workInProgress);1365        if (enableViewTransition) {1366          // Host Components act as their own View Transitions which doesn't run enter/exit animations.1367          // We clear any ViewTransitionStatic flag bubbled from inner View Transitions.1368          workInProgress.subtreeFlags &= ~ViewTransitionStatic;1369        }1370        return null;1371      }1372      // Fall through1373    }1374    case HostComponent: {1375      popHostContext(workInProgress);1376      const type = workInProgress.type;1377      if (current !== null && workInProgress.stateNode != null) {1378        updateHostComponent(1379          current,1380          workInProgress,1381          type,1382          newProps,1383          renderLanes,1384        );1385      } else {1386        if (!newProps) {1387          if (workInProgress.stateNode === null) {1388            throw new Error(1389              'We must have new props for new mounts. This error is likely ' +1390                'caused by a bug in React. Please file an issue.',1391            );1392          }13931394          // This can happen when we abort work.1395          bubbleProperties(workInProgress);1396          if (enableViewTransition) {1397            // Host Components act as their own View Transitions which doesn't run enter/exit animations.1398            // We clear any ViewTransitionStatic flag bubbled from inner View Transitions.1399            workInProgress.subtreeFlags &= ~ViewTransitionStatic;1400          }1401          return null;1402        }14031404        const currentHostContext = getHostContext();1405        // TODO: Move createInstance to beginWork and keep it on a context1406        // "stack" as the parent. Then append children as we go in beginWork1407        // or completeWork depending on whether we want to add them top->down or1408        // bottom->up. Top->down is faster in IE11.1409        const wasHydrated = popHydrationState(workInProgress);1410        if (wasHydrated) {1411          // TODO: Move this and createInstance step into the beginPhase1412          // to consolidate.1413          prepareToHydrateHostInstance(workInProgress, currentHostContext);1414          if (1415            finalizeHydratedChildren(1416              workInProgress.stateNode,1417              type,1418              newProps,1419              currentHostContext,1420            )1421          ) {1422            workInProgress.flags |= Hydrate;1423          }1424        } else {1425          const rootContainerInstance = getRootHostContainer();1426          const instance = createInstance(1427            type,1428            newProps,1429            rootContainerInstance,1430            currentHostContext,1431            workInProgress,1432          );1433          // TODO: For persistent renderers, we should pass children as part1434          // of the initial instance creation1435          markCloned(workInProgress);1436          appendAllChildren(instance, workInProgress, false, false);1437          workInProgress.stateNode = instance;14381439          // Certain renderers require commit-time effects for initial mount.1440          // (eg DOM renderer supports auto-focus for certain elements).1441          // Make sure such renderers get scheduled for later work.1442          if (1443            finalizeInitialChildren(1444              instance,1445              type,1446              newProps,1447              currentHostContext,1448            )1449          ) {1450            markUpdate(workInProgress);1451          }1452        }1453      }1454      bubbleProperties(workInProgress);1455      if (enableViewTransition) {1456        // Host Components act as their own View Transitions which doesn't run enter/exit animations.1457        // We clear any ViewTransitionStatic flag bubbled from inner View Transitions.1458        workInProgress.subtreeFlags &= ~ViewTransitionStatic;1459      }14601461      // This must come at the very end of the complete phase, because it might1462      // throw to suspend, and if the resource immediately loads, the work loop1463      // will resume rendering as if the work-in-progress completed. So it must1464      // fully complete.1465      preloadInstanceAndSuspendIfNeeded(1466        workInProgress,1467        workInProgress.type,1468        current === null ? null : current.memoizedProps,1469        workInProgress.pendingProps,1470        renderLanes,1471      );1472      return null;1473    }1474    case HostText: {1475      const newText = newProps;1476      if (current && workInProgress.stateNode != null) {1477        const oldText = current.memoizedProps;1478        // If we have an alternate, that means this is an update and we need1479        // to schedule a side-effect to do the updates.1480        updateHostText(current, workInProgress, oldText, newText);1481      } else {1482        if (typeof newText !== 'string') {1483          if (workInProgress.stateNode === null) {1484            throw new Error(1485              'We must have new props for new mounts. This error is likely ' +1486                'caused by a bug in React. Please file an issue.',1487            );1488          }1489          // This can happen when we abort work.1490        }1491        const rootContainerInstance = getRootHostContainer();1492        const currentHostContext = getHostContext();1493        const wasHydrated = popHydrationState(workInProgress);1494        if (wasHydrated) {1495          prepareToHydrateHostTextInstance(workInProgress);1496        } else {1497          markCloned(workInProgress);1498          workInProgress.stateNode = createTextInstance(1499            newText,1500            rootContainerInstance,1501            currentHostContext,1502            workInProgress,1503          );1504        }1505      }1506      bubbleProperties(workInProgress);1507      return null;1508    }1509    case ActivityComponent: {1510      const nextState: null | ActivityState = workInProgress.memoizedState;15111512      if (current === null || current.memoizedState !== null) {1513        const fallthroughToNormalOffscreenPath =1514          completeDehydratedActivityBoundary(1515            current,1516            workInProgress,1517            nextState,1518          );1519        if (!fallthroughToNormalOffscreenPath) {1520          if (workInProgress.flags & ForceClientRender) {1521            popSuspenseHandler(workInProgress);1522            // Special case. There were remaining unhydrated nodes. We treat1523            // this as a mismatch. Revert to client rendering.1524            return workInProgress;1525          } else {1526            popSuspenseHandler(workInProgress);1527            // Did not finish hydrating, either because this is the initial1528            // render or because something suspended.1529            return null;1530          }1531        }15321533        if ((workInProgress.flags & DidCapture) !== NoFlags) {1534          // We called retryActivityComponentWithoutHydrating and tried client rendering1535          // but now we suspended again. We should never arrive here because we should1536          // not have pushed a suspense handler during that second pass and it should1537          // instead have suspended above.1538          throw new Error(1539            'Client rendering an Activity suspended it again. This is a bug in React.',1540          );1541        }15421543        // Continue with the normal Activity path.1544      }15451546      bubbleProperties(workInProgress);1547      return null;1548    }1549    case SuspenseComponent: {1550      const nextState: null | SuspenseState = workInProgress.memoizedState;15511552      // Special path for dehydrated boundaries. We may eventually move this1553      // to its own fiber type so that we can add other kinds of hydration1554      // boundaries that aren't associated with a Suspense tree. In anticipation1555      // of such a refactor, all the hydration logic is contained in1556      // this branch.1557      if (1558        current === null ||1559        (current.memoizedState !== null &&1560          current.memoizedState.dehydrated !== null)1561      ) {1562        const fallthroughToNormalSuspensePath =1563          completeDehydratedSuspenseBoundary(1564            current,1565            workInProgress,1566            nextState,1567          );1568        if (!fallthroughToNormalSuspensePath) {1569          if (workInProgress.flags & ForceClientRender) {1570            popSuspenseHandler(workInProgress);1571            // Special case. There were remaining unhydrated nodes. We treat1572            // this as a mismatch. Revert to client rendering.1573            return workInProgress;1574          } else {1575            popSuspenseHandler(workInProgress);1576            // Did not finish hydrating, either because this is the initial1577            // render or because something suspended.1578            return null;1579          }1580        }15811582        // Continue with the normal Suspense path.1583      }15841585      popSuspenseHandler(workInProgress);15861587      if ((workInProgress.flags & DidCapture) !== NoFlags) {1588        // Something suspended. Re-render with the fallback children.1589        workInProgress.lanes = renderLanes;1590        if (1591          enableProfilerTimer &&1592          (workInProgress.mode & ProfileMode) !== NoMode1593        ) {1594          transferActualDuration(workInProgress);1595        }1596        // Don't bubble properties in this case.1597        return workInProgress;1598      }15991600      const nextDidTimeout = nextState !== null;1601      const prevDidTimeout =1602        current !== null &&1603        (current.memoizedState as null | SuspenseState) !== null;16041605      if (nextDidTimeout) {1606        const offscreenFiber: Fiber = workInProgress.child as any;1607        let previousCache: Cache | null = null;1608        if (1609          offscreenFiber.alternate !== null &&1610          offscreenFiber.alternate.memoizedState !== null &&1611          offscreenFiber.alternate.memoizedState.cachePool !== null1612        ) {1613          previousCache = offscreenFiber.alternate.memoizedState.cachePool.pool;1614        }1615        let cache: Cache | null = null;1616        if (1617          offscreenFiber.memoizedState !== null &&1618          offscreenFiber.memoizedState.cachePool !== null1619        ) {1620          cache = offscreenFiber.memoizedState.cachePool.pool;1621        }1622        if (cache !== previousCache) {1623          // Run passive effects to retain/release the cache.1624          offscreenFiber.flags |= Passive;1625        }1626      }16271628      // If the suspended state of the boundary changes, we need to schedule1629      // a passive effect, which is when we process the transitions1630      if (nextDidTimeout !== prevDidTimeout) {1631        if (enableTransitionTracing) {1632          const offscreenFiber: Fiber = workInProgress.child as any;1633          offscreenFiber.flags |= Passive;1634        }16351636        // If the suspended state of the boundary changes, we need to schedule1637        // an effect to toggle the subtree's visibility. When we switch from1638        // fallback -> primary, the inner Offscreen fiber schedules this effect1639        // as part of its normal complete phase. But when we switch from1640        // primary -> fallback, the inner Offscreen fiber does not have a complete1641        // phase. So we need to schedule its effect here.1642        //1643        // We also use this flag to connect/disconnect the effects, but the same1644        // logic applies: when re-connecting, the Offscreen fiber's complete1645        // phase will handle scheduling the effect. It's only when the fallback1646        // is active that we have to do anything special.1647        if (nextDidTimeout) {1648          const offscreenFiber: Fiber = workInProgress.child as any;1649          offscreenFiber.flags |= Visibility;1650        }1651      }16521653      const retryQueue: RetryQueue | null = workInProgress.updateQueue as any;1654      scheduleRetryEffect(workInProgress, retryQueue);16551656      if (1657        enableSuspenseCallback &&1658        workInProgress.updateQueue !== null &&1659        workInProgress.memoizedProps.suspenseCallback != null1660      ) {1661        // Always notify the callback1662        // TODO: Move to passive phase1663        workInProgress.flags |= Update;1664      }1665      bubbleProperties(workInProgress);1666      if (enableProfilerTimer) {1667        if ((workInProgress.mode & ProfileMode) !== NoMode) {1668          if (nextDidTimeout) {1669            // Don't count time spent in a timed out Suspense subtree as part of the base duration.1670            const primaryChildFragment = workInProgress.child;1671            if (primaryChildFragment !== null) {1672              // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator1673              workInProgress.treeBaseDuration -=1674                primaryChildFragment.treeBaseDuration as any as number;1675            }1676          }1677        }1678      }1679      return null;1680    }1681    case HostPortal:1682      popHostContainer(workInProgress);1683      updateHostContainer(current, workInProgress);1684      if (current === null) {1685        preparePortalMount(workInProgress.stateNode.containerInfo);1686      }1687      workInProgress.flags |= PortalStatic;1688      bubbleProperties(workInProgress);1689      return null;1690    case ContextProvider:1691      // Pop provider fiber1692      const context: ReactContext<any> = workInProgress.type;1693      popProvider(context, workInProgress);1694      bubbleProperties(workInProgress);1695      return null;1696    case IncompleteClassComponent: {1697      if (disableLegacyMode) {1698        break;1699      }1700      // Same as class component case. I put it down here so that the tags are1701      // sequential to ensure this switch is compiled to a jump table.1702      const Component = workInProgress.type;1703      if (isLegacyContextProvider(Component)) {1704        popLegacyContext(workInProgress);1705      }1706      bubbleProperties(workInProgress);1707      return null;1708    }1709    case SuspenseListComponent: {1710      popSuspenseListContext(workInProgress);17111712      const renderState: null | SuspenseListRenderState =1713        workInProgress.memoizedState;17141715      if (renderState === null) {1716        // We're running in the default, "independent" mode.1717        // We don't do anything in this mode.1718        bubbleProperties(workInProgress);1719        return null;1720      }17211722      let didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;17231724      const renderedTail = renderState.rendering;1725      if (renderedTail === null) {1726        // We just rendered the head.1727        if (!didSuspendAlready) {1728          // This is the first pass. We need to figure out if anything is still1729          // suspended in the rendered set.17301731          // If new content unsuspended, but there's still some content that1732          // didn't. Then we need to do a second pass that forces everything1733          // to keep showing their fallbacks.17341735          // We might be suspended if something in this render pass suspended, or1736          // something in the previous committed pass suspended. Otherwise,1737          // there's no chance so we can skip the expensive call to1738          // findFirstSuspended.1739          const cannotBeSuspended =1740            renderHasNotSuspendedYet() &&1741            (current === null || (current.flags & DidCapture) === NoFlags);1742          if (!cannotBeSuspended) {1743            let row = workInProgress.child;1744            while (row !== null) {1745              const suspended = findFirstSuspended(row);1746              if (suspended !== null) {1747                didSuspendAlready = true;1748                workInProgress.flags |= DidCapture;1749                cutOffTailIfNeeded(renderState, false);17501751                // If this is a newly suspended tree, it might not get committed as1752                // part of the second pass. In that case nothing will subscribe to1753                // its thenables. Instead, we'll transfer its thenables to the1754                // SuspenseList so that it can retry if they resolve.1755                // There might be multiple of these in the list but since we're1756                // going to wait for all of them anyway, it doesn't really matter1757                // which ones gets to ping. In theory we could get clever and keep1758                // track of how many dependencies remain but it gets tricky because1759                // in the meantime, we can add/remove/change items and dependencies.1760                // We might bail out of the loop before finding any but that1761                // doesn't matter since that means that the other boundaries that1762                // we did find already has their listeners attached.1763                const retryQueue: RetryQueue | null =1764                  suspended.updateQueue as any;1765                workInProgress.updateQueue = retryQueue;1766                scheduleRetryEffect(workInProgress, retryQueue);17671768                // Rerender the whole list, but this time, we'll force fallbacks1769                // to stay in place.1770                // Reset the effect flags before doing the second pass since that's now invalid.1771                // Reset the child fibers to their original state.1772                workInProgress.subtreeFlags = NoFlags;1773                resetChildFibers(workInProgress, renderLanes);17741775                // Set up the Suspense List Context to force suspense and1776                // immediately rerender the children.1777                pushSuspenseListContext(1778                  workInProgress,1779                  setShallowSuspenseListContext(1780                    suspenseStackCursor.current,1781                    ForceSuspenseFallback,1782                  ),1783                );1784                if (getIsHydrating()) {1785                  // Re-apply tree fork since we popped the tree fork context in the beginning of this function.1786                  pushTreeFork(workInProgress, renderState.treeForkCount);1787                }1788                // Don't bubble properties in this case.1789                return workInProgress.child;1790              }1791              row = row.sibling;1792            }1793          }17941795          if (renderState.tail !== null && now() > getRenderTargetTime()) {1796            // We have already passed our CPU deadline but we still have rows1797            // left in the tail. We'll just give up further attempts to render1798            // the main content and only render fallbacks.1799            workInProgress.flags |= DidCapture;1800            didSuspendAlready = true;18011802            cutOffTailIfNeeded(renderState, false);18031804            // Since nothing actually suspended, there will nothing to ping this1805            // to get it started back up to attempt the next item. While in terms1806            // of priority this work has the same priority as this current render,1807            // it's not part of the same transition once the transition has1808            // committed. If it's sync, we still want to yield so that it can be1809            // painted. Conceptually, this is really the same as pinging.1810            // We can use any RetryLane even if it's the one currently rendering1811            // since we're leaving it behind on this node.1812            workInProgress.lanes = SomeRetryLane;1813          }1814        } else {1815          cutOffTailIfNeeded(renderState, false);1816        }1817        // Next we're going to render the tail.1818      } else {1819        // Append the rendered row to the child list.1820        if (!didSuspendAlready) {1821          const suspended = findFirstSuspended(renderedTail);1822          if (suspended !== null) {1823            workInProgress.flags |= DidCapture;1824            didSuspendAlready = true;18251826            // Ensure we transfer the update queue to the parent so that it doesn't1827            // get lost if this row ends up dropped during a second pass.1828            const retryQueue: RetryQueue | null = suspended.updateQueue as any;1829            workInProgress.updateQueue = retryQueue;1830            scheduleRetryEffect(workInProgress, retryQueue);18311832            cutOffTailIfNeeded(renderState, true);1833            // This might have been modified.1834            if (1835              renderState.tail === null &&1836              renderState.tailMode !== 'collapsed' &&1837              renderState.tailMode !== 'visible' &&1838              !renderedTail.alternate &&1839              !getIsHydrating() // We don't cut it if we're hydrating.1840            ) {1841              // We're done.1842              bubbleProperties(workInProgress);1843              return null;1844            }1845          } else if (1846            // The time it took to render last row is greater than the remaining1847            // time we have to render. So rendering one more row would likely1848            // exceed it.1849            now() * 2 - renderState.renderingStartTime >1850              getRenderTargetTime() &&1851            renderLanes !== OffscreenLane1852          ) {1853            // We have now passed our CPU deadline and we'll just give up further1854            // attempts to render the main content and only render fallbacks.1855            // The assumption is that this is usually faster.1856            workInProgress.flags |= DidCapture;1857            didSuspendAlready = true;18581859            cutOffTailIfNeeded(renderState, false);18601861            // Since nothing actually suspended, there will nothing to ping this1862            // to get it started back up to attempt the next item. While in terms1863            // of priority this work has the same priority as this current render,1864            // it's not part of the same transition once the transition has1865            // committed. If it's sync, we still want to yield so that it can be1866            // painted. Conceptually, this is really the same as pinging.1867            // We can use any RetryLane even if it's the one currently rendering1868            // since we're leaving it behind on this node.1869            workInProgress.lanes = SomeRetryLane;1870          }1871        }1872        if (renderState.isBackwards) {1873          // Append to the beginning of the list.1874          renderedTail.sibling = workInProgress.child;1875          workInProgress.child = renderedTail;1876        } else {1877          const previousSibling = renderState.last;1878          if (previousSibling !== null) {1879            previousSibling.sibling = renderedTail;1880          } else {1881            workInProgress.child = renderedTail;1882          }1883          renderState.last = renderedTail;1884        }1885      }18861887      if (renderState.tail !== null) {1888        // We still have tail rows to render.1889        // Pop a row.1890        // TODO: Consider storing the first of the new mount tail in the state so1891        // that we don't have to recompute this for every row in the list.1892        const next = renderState.tail;1893        const onlyNewMounts = isOnlyNewMounts(next);1894        renderState.rendering = next;1895        renderState.tail = next.sibling;1896        renderState.renderingStartTime = now();1897        next.sibling = null;18981899        // Restore the context.1900        // TODO: We can probably just avoid popping it instead and only1901        // setting it the first time we go from not suspended to suspended.1902        let suspenseContext = suspenseStackCursor.current;1903        if (didSuspendAlready) {1904          suspenseContext = setShallowSuspenseListContext(1905            suspenseContext,1906            ForceSuspenseFallback,1907          );1908        } else {1909          suspenseContext =1910            setDefaultShallowSuspenseListContext(suspenseContext);1911        }1912        if (1913          renderState.tailMode === 'visible' ||1914          renderState.tailMode === 'collapsed' ||1915          !onlyNewMounts ||1916          // TODO: While hydrating, we still let it suspend the parent. Tail mode hidden has broken1917          // hydration anyway right now but this preserves the previous semantics out of caution.1918          // Once proper hydration is implemented, this special case should be removed as it should1919          // never be needed.1920          getIsHydrating()1921        ) {1922          pushSuspenseListContext(workInProgress, suspenseContext);1923        } else {1924          // If we are rendering in 'hidden' (default) tail mode, then we if we suspend in the1925          // tail itself, we can delete it rather than suspend the parent. So we act as a catch in that1926          // case. For 'collapsed' we need to render at least one in suspended state, after which we'll1927          // have cut off the rest to never attempt it so it never hits this case.1928          // If this is an updated node, we cannot delete it from the tail so it's effectively visible.1929          // As a consequence, if it resuspends it actually suspends the parent by taking the other path.1930          pushSuspenseListCatch(workInProgress, suspenseContext);1931        }1932        // Do a pass over the next row.1933        if (getIsHydrating()) {1934          // Re-apply tree fork since we popped the tree fork context in the beginning of this function.1935          pushTreeFork(workInProgress, renderState.treeForkCount);1936        }1937        // Don't bubble properties in this case.1938        return next;1939      }1940      bubbleProperties(workInProgress);1941      return null;1942    }1943    case ScopeComponent: {1944      if (enableScopeAPI) {1945        if (current === null) {1946          const scopeInstance: ReactScopeInstance = createScopeInstance();1947          workInProgress.stateNode = scopeInstance;1948          prepareScopeUpdate(scopeInstance, workInProgress);1949          if (workInProgress.ref !== null) {1950            // Scope components always do work in the commit phase if there's a1951            // ref attached.1952            markUpdate(workInProgress);1953          }1954        } else {1955          if (workInProgress.ref !== null) {1956            // Scope components always do work in the commit phase if there's a1957            // ref attached.1958            markUpdate(workInProgress);1959          }1960        }1961        bubbleProperties(workInProgress);1962        return null;1963      }1964      break;1965    }1966    case OffscreenComponent:1967    case LegacyHiddenComponent: {1968      popSuspenseHandler(workInProgress);1969      popHiddenContext(workInProgress);1970      const nextState: OffscreenState | null = workInProgress.memoizedState;1971      const nextIsHidden = nextState !== null;19721973      // Schedule a Visibility effect if the visibility has changed1974      if (enableLegacyHidden && workInProgress.tag === LegacyHiddenComponent) {1975        // LegacyHidden doesn't do any hiding — it only pre-renders.1976      } else {1977        if (current !== null) {1978          const prevState: OffscreenState | null = current.memoizedState;1979          const prevIsHidden = prevState !== null;1980          if (prevIsHidden !== nextIsHidden) {1981            workInProgress.flags |= Visibility;1982          }1983        } else {1984          // On initial mount, we only need a Visibility effect if the tree1985          // is hidden.1986          if (nextIsHidden) {1987            workInProgress.flags |= Visibility;1988          }1989        }1990      }19911992      if (1993        !nextIsHidden ||1994        (!disableLegacyMode &&1995          (workInProgress.mode & ConcurrentMode) === NoMode)1996      ) {1997        bubbleProperties(workInProgress);1998      } else {1999        // Don't bubble properties for hidden children unless we're rendering2000        // at offscreen priority.

Code quality findings 100

Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const didBailout = current !== null && current.child === completedWork.child;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((completedWork.flags & ChildDeletion) !== NoFlags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(child.flags & checkedFlags) !== NoFlags ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(child.subtreeFlags & checkedFlags) !== NoFlags
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (node !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node.tag === HostComponent || node.tag === HostText) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
node.tag === HostPortal ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(supportsSingletons ? node.tag === HostSingleton : false)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node === workInProgress) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (node.sibling === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node.return === null || node.return === workInProgress) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (node !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node.tag === HostComponent) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.tag === HostText) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.tag === HostPortal) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
node.tag === OffscreenComponent &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
node.memoizedState !== null
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node === workInProgress) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (node.sibling === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node.return === null || node.return === workInProgress) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (node !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node.tag === HostComponent) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.tag === HostText) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.tag === HostPortal) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
node.tag === OffscreenComponent &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
node.memoizedState !== null
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (node.child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node === workInProgress) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (node.sibling === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (node.return === null || node.return === workInProgress) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (oldProps === newProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (!requiresClone && oldProps === newProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (newInstance === currentInstance) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(workInProgress.mode & SuspenseyImagesMode) !== NoMode) &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(oldProps === null
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (wakeables !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
workInProgress.tag !== OffscreenComponent
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (oldText !== newText) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (oldText !== newText) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (renderState.tailMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (tailNode !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (tailNode.alternate !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (lastTailNode === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (!hasRenderedATailFallback && renderState.tail !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (tailNode !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (tailNode.alternate !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (lastTailNode === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (fiber !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (fiber.alternate !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
completedWork.alternate !== null &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
completedWork.alternate.child === completedWork.child;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (child !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nextState !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.mode & ProfileMode) !== NoMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const isTimedOutSuspense = nextState !== null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (primaryChildFragment !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.flags & DidCapture) === NoFlags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.mode & ProfileMode) !== NoMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const isTimedOutSuspense = nextState !== null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (primaryChildFragment !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null && current.memoizedState !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nextState !== null && nextState.dehydrated !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.mode & ProfileMode) !== NoMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const isTimedOutSuspense = nextState !== null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (primaryChildFragment !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.flags & DidCapture) === NoFlags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.mode & ProfileMode) !== NoMode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const isTimedOutSuspense = nextState !== null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (primaryChildFragment !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null && current.memoizedState !== null) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (workInProgress.tag) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (transitions !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (cache !== previousCache) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current === null || current.child === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(workInProgress.flags & ForceClientRender) !== NoFlags
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nextResource !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nextResource !== current.memoizedState) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (oldProps !== newProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null && workInProgress.stateNode != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (current !== null && workInProgress.stateNode != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (oldProps !== newProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (workInProgress.stateNode === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null && workInProgress.stateNode != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (current !== null && workInProgress.stateNode != null) {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.