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