1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import type {11 ReactConsumerType,12 ReactContext,13 ReactNodeList,14 ViewTransitionProps,15 ActivityProps,16 SuspenseProps,17 SuspenseListProps,18 SuspenseListRevealOrder,19 SuspenseListTailMode,20 TracingMarkerProps,21 CacheProps,22 ProfilerProps,23} from 'shared/ReactTypes';24import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';25import type {Fiber, FiberRoot} from './ReactInternalTypes';26import type {TypeOfMode} from './ReactTypeOfMode';27import type {Lanes, Lane} from './ReactFiberLane';28import type {ActivityState} from './ReactFiberActivityComponent';29import type {30 SuspenseState,31 SuspenseListRenderState,32} from './ReactFiberSuspenseComponent';33import type {SuspenseContext} from './ReactFiberSuspenseContext';34import type {35 LegacyHiddenProps,36 OffscreenProps,37 OffscreenState,38 OffscreenQueue,39 OffscreenInstance,40} from './ReactFiberOffscreenComponent';41import type {42 Cache,43 CacheComponentState,44 SpawnedCachePool,45} from './ReactFiberCacheComponent';46import type {UpdateQueue} from './ReactFiberClassUpdateQueue';47import type {RootState} from './ReactFiberRoot';48import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';49import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';5051import {52 markComponentRenderStarted,53 markComponentRenderStopped,54 setIsStrictModeForDevtools,55} from './ReactFiberDevToolsHook';56import {57 FunctionComponent,58 ClassComponent,59 HostRoot,60 HostComponent,61 HostHoistable,62 HostSingleton,63 HostText,64 HostPortal,65 ForwardRef,66 Fragment,67 Mode,68 ContextProvider,69 ContextConsumer,70 Profiler,71 SuspenseComponent,72 SuspenseListComponent,73 MemoComponent,74 SimpleMemoComponent,75 LazyComponent,76 IncompleteClassComponent,77 IncompleteFunctionComponent,78 ScopeComponent,79 OffscreenComponent,80 LegacyHiddenComponent,81 CacheComponent,82 TracingMarkerComponent,83 Throw,84 ViewTransitionComponent,85 ActivityComponent,86} from './ReactWorkTags';87import {88 NoFlags,89 PerformedWork,90 Placement,91 PlacementDEV,92 Hydrating,93 Callback,94 ContentReset,95 DidCapture,96 Update,97 Ref,98 RefStatic,99 ChildDeletion,100 ForceUpdateForLegacySuspense,101 StaticMask,102 ShouldCapture,103 ForceClientRender,104 Passive,105 DidDefer,106 ViewTransitionNamedStatic,107 ViewTransitionNamedMount,108 LayoutStatic,109} from './ReactFiberFlags';110import {111 disableLegacyContext,112 disableLegacyContextForFunctionComponents,113 enableProfilerCommitHooks,114 enableProfilerTimer,115 enableScopeAPI,116 enableSchedulingProfiler,117 enableTransitionTracing,118 enableLegacyHidden,119 enableCPUSuspense,120 disableLegacyMode,121 enableViewTransition,122 enableFragmentRefs,123} from 'shared/ReactFeatureFlags';124import shallowEqual from 'shared/shallowEqual';125import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';126import getComponentNameFromType from 'shared/getComponentNameFromType';127import ReactStrictModeWarnings from './ReactStrictModeWarnings';128import {129 REACT_LAZY_TYPE,130 REACT_FORWARD_REF_TYPE,131 REACT_MEMO_TYPE,132 REACT_CONTEXT_TYPE,133} from 'shared/ReactSymbols';134import {setCurrentFiber} from './ReactCurrentFiber';135import {136 resolveFunctionForHotReloading,137 resolveForwardRefForHotReloading,138 resolveClassForHotReloading,139} from './ReactFiberHotReloading';140141import {142 mountChildFibers,143 reconcileChildFibers,144 cloneChildFibers,145 validateSuspenseListChildren,146} from './ReactChildFiber';147import {148 processUpdateQueue,149 cloneUpdateQueue,150 initializeUpdateQueue,151 enqueueCapturedUpdate,152 suspendIfUpdateReadFromEntangledAsyncAction,153} from './ReactFiberClassUpdateQueue';154import {155 NoLane,156 NoLanes,157 OffscreenLane,158 DefaultLane,159 SomeRetryLane,160 includesSomeLane,161 includesOnlyRetries,162 laneToLanes,163 removeLanes,164 mergeLanes,165 getBumpedLaneForHydration,166 pickArbitraryLane,167} from './ReactFiberLane';168import {169 ConcurrentMode,170 NoMode,171 ProfileMode,172 StrictLegacyMode,173} from './ReactTypeOfMode';174import {175 shouldSetTextContent,176 isSuspenseInstancePending,177 isSuspenseInstanceFallback,178 getSuspenseInstanceFallbackErrorDetails,179 supportsHydration,180 supportsResources,181 supportsSingletons,182 isPrimaryRenderer,183 getResource,184 createHoistableInstance,185 HostTransitionContext,186} from './ReactFiberConfig';187import type {ActivityInstance, SuspenseInstance} from './ReactFiberConfig';188import {shouldError, shouldSuspend} from './ReactFiberReconciler';189import {190 pushHostContext,191 pushHostContainer,192 getRootHostContainer,193} from './ReactFiberHostContext';194import {195 suspenseStackCursor,196 pushSuspenseListContext,197 ForceSuspenseFallback,198 hasSuspenseListContext,199 setDefaultShallowSuspenseListContext,200 setShallowSuspenseListContext,201 pushPrimaryTreeSuspenseHandler,202 pushFallbackTreeSuspenseHandler,203 pushDehydratedActivitySuspenseHandler,204 pushOffscreenSuspenseHandler,205 reuseSuspenseHandlerOnStack,206 popSuspenseHandler,207} from './ReactFiberSuspenseContext';208import {209 pushHiddenContext,210 reuseHiddenContextOnStack,211} from './ReactFiberHiddenContext';212import {findFirstSuspended} from './ReactFiberSuspenseComponent';213import {214 pushProvider,215 propagateContextChange,216 lazilyPropagateParentContextChanges,217 propagateParentContextChangesToDeferredTree,218 checkIfContextChanged,219 readContext,220 prepareToReadContext,221 scheduleContextWorkOnParentPath,222} from './ReactFiberNewContext';223import {224 renderWithHooks,225 checkDidRenderIdHook,226 bailoutHooks,227 replaySuspendedComponentWithHooks,228 renderTransitionAwareHostComponentWithHooks,229} from './ReactFiberHooks';230import {stopProfilerTimerIfRunning} from './ReactProfilerTimer';231import {232 getMaskedContext,233 getUnmaskedContext,234 hasContextChanged as hasLegacyContextChanged,235 pushContextProvider as pushLegacyContextProvider,236 isContextProvider as isLegacyContextProvider,237 pushTopLevelContextObject,238 invalidateContextProvider,239} from './ReactFiberLegacyContext';240import {241 getIsHydrating,242 enterHydrationState,243 reenterHydrationStateFromDehydratedActivityInstance,244 reenterHydrationStateFromDehydratedSuspenseInstance,245 resetHydrationState,246 claimHydratableSingleton,247 tryToClaimNextHydratableInstance,248 tryToClaimNextHydratableTextInstance,249 claimNextHydratableActivityInstance,250 claimNextHydratableSuspenseInstance,251 warnIfHydrating,252 queueHydrationError,253} from './ReactFiberHydrationContext';254import {255 constructClassInstance,256 mountClassInstance,257 resumeMountClassInstance,258 updateClassInstance,259 resolveClassComponentProps,260} from './ReactFiberClassComponent';261import {262 createFiberFromTypeAndProps,263 createFiberFromFragment,264 createFiberFromOffscreen,265 createWorkInProgress,266 isSimpleFunctionComponent,267 isFunctionClassComponent,268} from './ReactFiber';269import {270 scheduleUpdateOnFiber,271 renderDidSuspendDelayIfPossible,272 markSkippedUpdateLanes,273 markRenderDerivedCause,274 getWorkInProgressRoot,275 peekDeferredLane,276} from './ReactFiberWorkLoop';277import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';278import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent';279import {280 createCapturedValueFromError,281 createCapturedValueAtFiber,282} from './ReactCapturedValue';283import {OffscreenVisible} from './ReactFiberOffscreenComponent';284import {285 createClassErrorUpdate,286 initializeClassErrorUpdate,287} from './ReactFiberThrow';288import {289 getForksAtLevel,290 isForkedChild,291 pushTreeId,292 pushMaterializedTreeId,293} from './ReactFiberTreeContext';294import {295 requestCacheFromPool,296 pushRootTransition,297 getSuspendedCache,298 pushTransition,299 getOffscreenDeferredCache,300 getPendingTransitions,301} from './ReactFiberTransition';302import {303 getMarkerInstances,304 pushMarkerInstance,305 pushRootMarkerInstance,306 TransitionTracingMarker,307} from './ReactFiberTracingMarkerComponent';308import {callComponentInDEV, callRenderInDEV} from './ReactFiberCallUserSpace';309import {resolveLazy} from './ReactFiberThenable';310311// A special exception that's used to unwind the stack when an update flows312// into a dehydrated boundary.313export const SelectiveHydrationException: mixed = new Error(314 "This is not a real error. It's an implementation detail of React's " +315 "selective hydration feature. If this leaks into userspace, it's a bug in " +316 'React. Please file an issue.',317);318319let didReceiveUpdate: boolean = false;320321let didWarnAboutBadClass;322let didWarnAboutContextTypeOnFunctionComponent;323let didWarnAboutContextTypes;324let didWarnAboutGetDerivedStateOnFunctionComponent;325export let didWarnAboutReassigningProps: boolean;326let didWarnAboutRevealOrder;327let didWarnAboutTailOptions;328let didWarnAboutClassNameOnViewTransition;329330if (__DEV__) {331 didWarnAboutBadClass = {} as {[string]: boolean};332 didWarnAboutContextTypeOnFunctionComponent = {} as {[string]: boolean};333 didWarnAboutContextTypes = {} as {[string]: boolean};334 didWarnAboutGetDerivedStateOnFunctionComponent = {} as {[string]: boolean};335 didWarnAboutReassigningProps = false;336 didWarnAboutRevealOrder = {} as {[string]: boolean};337 didWarnAboutTailOptions = {} as {[string]: boolean};338 didWarnAboutClassNameOnViewTransition = {} as {[string]: boolean};339}340341export function reconcileChildren(342 current: Fiber | null,343 workInProgress: Fiber,344 nextChildren: any,345 renderLanes: Lanes,346) {347 if (current === null) {348 // If this is a fresh new component that hasn't been rendered yet, we349 // won't update its child set by applying minimal side-effects. Instead,350 // we will add them all to the child before it gets rendered. That means351 // we can optimize this reconciliation pass by not tracking side-effects.352 workInProgress.child = mountChildFibers(353 workInProgress,354 null,355 nextChildren,356 renderLanes,357 );358 } else {359 // If the current child is the same as the work in progress, it means that360 // we haven't yet started any work on these children. Therefore, we use361 // the clone algorithm to create a copy of all the current children.362363 // If we had any progressed work already, that is invalid at this point so364 // let's throw it out.365 workInProgress.child = reconcileChildFibers(366 workInProgress,367 current.child,368 nextChildren,369 renderLanes,370 );371 }372}373374function forceUnmountCurrentAndReconcile(375 current: Fiber,376 workInProgress: Fiber,377 nextChildren: any,378 renderLanes: Lanes,379) {380 // This function is fork of reconcileChildren. It's used in cases where we381 // want to reconcile without matching against the existing set. This has the382 // effect of all current children being unmounted; even if the type and key383 // are the same, the old child is unmounted and a new child is created.384 //385 // To do this, we're going to go through the reconcile algorithm twice. In386 // the first pass, we schedule a deletion for all the current children by387 // passing null.388 workInProgress.child = reconcileChildFibers(389 workInProgress,390 current.child,391 null,392 renderLanes,393 );394 // In the second pass, we mount the new children. The trick here is that we395 // pass null in place of where we usually pass the current child set. This has396 // the effect of remounting all children regardless of whether their397 // identities match.398 workInProgress.child = reconcileChildFibers(399 workInProgress,400 null,401 nextChildren,402 renderLanes,403 );404}405406function updateForwardRef(407 current: Fiber | null,408 workInProgress: Fiber,409 Component: any,410 nextProps: any,411 renderLanes: Lanes,412) {413 // TODO: current can be non-null here even if the component414 // hasn't yet mounted. This happens after the first render suspends.415 // We'll need to figure out if this is fine or can cause issues.416 const render = Component.render;417 const ref = workInProgress.ref;418419 let propsWithoutRef;420 if ('ref' in nextProps) {421 // `ref` is just a prop now, but `forwardRef` expects it to not appear in422 // the props object. This used to happen in the JSX runtime, but now we do423 // it here.424 propsWithoutRef = {} as {[string]: any};425 for (const key in nextProps) {426 // Since `ref` should only appear in props via the JSX transform, we can427 // assume that this is a plain object. So we don't need a428 // hasOwnProperty check.429 if (key !== 'ref') {430 propsWithoutRef[key] = nextProps[key];431 }432 }433 } else {434 propsWithoutRef = nextProps;435 }436437 // The rest is a fork of updateFunctionComponent438 prepareToReadContext(workInProgress, renderLanes);439 if (enableSchedulingProfiler) {440 markComponentRenderStarted(workInProgress);441 }442443 const nextChildren = renderWithHooks(444 current,445 workInProgress,446 render,447 propsWithoutRef,448 ref,449 renderLanes,450 );451 const hasId = checkDidRenderIdHook();452453 if (enableSchedulingProfiler) {454 markComponentRenderStopped();455 }456457 if (current !== null && !didReceiveUpdate) {458 bailoutHooks(current, workInProgress, renderLanes);459 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);460 }461462 if (getIsHydrating() && hasId) {463 pushMaterializedTreeId(workInProgress);464 }465466 // React DevTools reads this flag.467 workInProgress.flags |= PerformedWork;468 reconcileChildren(current, workInProgress, nextChildren, renderLanes);469 return workInProgress.child;470}471472function updateMemoComponent(473 current: Fiber | null,474 workInProgress: Fiber,475 Component: any,476 nextProps: any,477 renderLanes: Lanes,478): null | Fiber {479 if (current === null) {480 const type = Component.type;481 if (isSimpleFunctionComponent(type) && Component.compare === null) {482 let resolvedType = type;483 if (__DEV__) {484 resolvedType = resolveFunctionForHotReloading(type);485 }486 // If this is a plain function component without default props,487 // and with only the default shallow comparison, we upgrade it488 // to a SimpleMemoComponent to allow fast path updates.489 workInProgress.tag = SimpleMemoComponent;490 workInProgress.type = resolvedType;491 if (__DEV__) {492 validateFunctionComponentInDev(workInProgress, type);493 }494 return updateSimpleMemoComponent(495 current,496 workInProgress,497 resolvedType,498 nextProps,499 renderLanes,500 );501 }502 const child = createFiberFromTypeAndProps(503 Component.type,504 null,505 nextProps,506 workInProgress,507 workInProgress.mode,508 renderLanes,509 );510 child.ref = workInProgress.ref;511 child.return = workInProgress;512 workInProgress.child = child;513 return child;514 }515 const currentChild = current.child as any as Fiber; // This is always exactly one child516 const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(517 current,518 renderLanes,519 );520 if (!hasScheduledUpdateOrContext) {521 // This will be the props with resolved defaultProps,522 // unlike current.memoizedProps which will be the unresolved ones.523 const prevProps = currentChild.memoizedProps;524 // Default to shallow comparison525 let compare = Component.compare;526 compare = compare !== null ? compare : shallowEqual;527 if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {528 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);529 }530 }531 // React DevTools reads this flag.532 workInProgress.flags |= PerformedWork;533 const newChild = createWorkInProgress(currentChild, nextProps);534 newChild.ref = workInProgress.ref;535 newChild.return = workInProgress;536 workInProgress.child = newChild;537 return newChild;538}539540function updateSimpleMemoComponent(541 current: Fiber | null,542 workInProgress: Fiber,543 Component: any,544 nextProps: any,545 renderLanes: Lanes,546): null | Fiber {547 // TODO: current can be non-null here even if the component548 // hasn't yet mounted. This happens when the inner render suspends.549 // We'll need to figure out if this is fine or can cause issues.550 if (current !== null) {551 const prevProps = current.memoizedProps;552 if (553 shallowEqual(prevProps, nextProps) &&554 current.ref === workInProgress.ref &&555 // Prevent bailout if the implementation changed due to hot reload.556 (__DEV__ ? workInProgress.type === current.type : true)557 ) {558 didReceiveUpdate = false;559560 // The props are shallowly equal. Reuse the previous props object, like we561 // would during a normal fiber bailout.562 //563 // We don't have strong guarantees that the props object is referentially564 // equal during updates where we can't bail out anyway — like if the props565 // are shallowly equal, but there's a local state or context update in the566 // same batch.567 //568 // However, as a principle, we should aim to make the behavior consistent569 // across different ways of memoizing a component. For example, React.memo570 // has a different internal Fiber layout if you pass a normal function571 // component (SimpleMemoComponent) versus if you pass a different type572 // like forwardRef (MemoComponent). But this is an implementation detail.573 // Wrapping a component in forwardRef (or React.lazy, etc) shouldn't574 // affect whether the props object is reused during a bailout.575 workInProgress.pendingProps = nextProps = prevProps;576577 if (!checkScheduledUpdateOrContext(current, renderLanes)) {578 // The pending lanes were cleared at the beginning of beginWork. We're579 // about to bail out, but there might be other lanes that weren't580 // included in the current render. Usually, the priority level of the581 // remaining updates is accumulated during the evaluation of the582 // component (i.e. when processing the update queue). But since since583 // we're bailing out early *without* evaluating the component, we need584 // to account for it here, too. Reset to the value of the current fiber.585 // NOTE: This only applies to SimpleMemoComponent, not MemoComponent,586 // because a MemoComponent fiber does not have hooks or an update queue;587 // rather, it wraps around an inner component, which may or may not588 // contains hooks.589 // TODO: Move the reset at in beginWork out of the common path so that590 // this is no longer necessary.591 workInProgress.lanes = current.lanes;592 return bailoutOnAlreadyFinishedWork(593 current,594 workInProgress,595 renderLanes,596 );597 } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {598 // This is a special case that only exists for legacy mode.599 // See https://github.com/facebook/react/pull/19216.600 didReceiveUpdate = true;601 }602 }603 }604 return updateFunctionComponent(605 current,606 workInProgress,607 Component,608 nextProps,609 renderLanes,610 );611}612613function updateOffscreenComponent(614 current: Fiber | null,615 workInProgress: Fiber,616 renderLanes: Lanes,617 nextProps: OffscreenProps,618) {619 const nextChildren = nextProps.children;620621 const prevState: OffscreenState | null =622 current !== null ? current.memoizedState : null;623624 if (current === null && workInProgress.stateNode === null) {625 // We previously reset the work-in-progress.626 // We need to create a new Offscreen instance.627 const primaryChildInstance: OffscreenInstance = {628 _visibility: OffscreenVisible,629 _pendingMarkers: null,630 _retryCache: null,631 _transitions: null,632 };633 workInProgress.stateNode = primaryChildInstance;634 }635636 if (637 nextProps.mode === 'hidden' ||638 (enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')639 ) {640 // Rendering a hidden tree.641642 const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;643 if (didSuspend) {644 // Something suspended inside a hidden tree645646 // Include the base lanes from the last render647 const nextBaseLanes =648 prevState !== null649 ? mergeLanes(prevState.baseLanes, renderLanes)650 : renderLanes;651652 let remainingChildLanes;653 if (current !== null) {654 // Reset to the current children655 let currentChild = (workInProgress.child = current.child);656657 // The current render suspended, but there may be other lanes with658 // pending work. We can't read `childLanes` from the current Offscreen659 // fiber because we reset it when it was deferred; however, we can read660 // the pending lanes from the child fibers.661 let currentChildLanes: Lanes = NoLanes;662 while (currentChild !== null) {663 currentChildLanes = mergeLanes(664 mergeLanes(currentChildLanes, currentChild.lanes),665 currentChild.childLanes,666 );667 currentChild = currentChild.sibling;668 }669 const lanesWeJustAttempted = nextBaseLanes;670 remainingChildLanes = removeLanes(671 currentChildLanes,672 lanesWeJustAttempted,673 );674 } else {675 remainingChildLanes = NoLanes;676 workInProgress.child = null;677 }678679 return deferHiddenOffscreenComponent(680 current,681 workInProgress,682 nextBaseLanes,683 renderLanes,684 remainingChildLanes,685 );686 }687688 if (689 !disableLegacyMode &&690 (workInProgress.mode & ConcurrentMode) === NoMode691 ) {692 // In legacy sync mode, don't defer the subtree. Render it now.693 // TODO: Consider how Offscreen should work with transitions in the future694 const nextState: OffscreenState = {695 baseLanes: NoLanes,696 cachePool: null,697 };698 workInProgress.memoizedState = nextState;699 // push the cache pool even though we're going to bail out700 // because otherwise there'd be a context mismatch701 if (current !== null) {702 pushTransition(workInProgress, null, null);703 }704 reuseHiddenContextOnStack(workInProgress);705 pushOffscreenSuspenseHandler(workInProgress);706 } else if (!includesSomeLane(renderLanes, OffscreenLane as Lane)) {707 // We're hidden, and we're not rendering at Offscreen. We will bail out708 // and resume this tree later.709710 // Schedule this fiber to re-render at Offscreen priority711712 const remainingChildLanes = (workInProgress.lanes =713 laneToLanes(OffscreenLane));714715 // Include the base lanes from the last render716 const nextBaseLanes =717 prevState !== null718 ? mergeLanes(prevState.baseLanes, renderLanes)719 : renderLanes;720721 return deferHiddenOffscreenComponent(722 current,723 workInProgress,724 nextBaseLanes,725 renderLanes,726 remainingChildLanes,727 );728 } else {729 // This is the second render. The surrounding visible content has already730 // committed. Now we resume rendering the hidden tree.731732 // Rendering at offscreen, so we can clear the base lanes.733 const nextState: OffscreenState = {734 baseLanes: NoLanes,735 cachePool: null,736 };737 workInProgress.memoizedState = nextState;738 if (current !== null) {739 // If the render that spawned this one accessed the cache pool, resume740 // using the same cache. Unless the parent changed, since that means741 // there was a refresh.742 const prevCachePool = prevState !== null ? prevState.cachePool : null;743 // TODO: Consider if and how Offscreen pre-rendering should744 // be attributed to the transition that spawned it745 pushTransition(workInProgress, prevCachePool, null);746 }747748 // Push the lanes that were skipped when we bailed out.749 if (prevState !== null) {750 pushHiddenContext(workInProgress, prevState);751 } else {752 reuseHiddenContextOnStack(workInProgress);753 }754 pushOffscreenSuspenseHandler(workInProgress);755 }756 } else {757 // Rendering a visible tree.758 if (prevState !== null) {759 // We're going from hidden -> visible.760 let prevCachePool = null;761 // If the render that spawned this one accessed the cache pool, resume762 // using the same cache. Unless the parent changed, since that means763 // there was a refresh.764 prevCachePool = prevState.cachePool;765766 let transitions = null;767 if (enableTransitionTracing) {768 // We have now gone from hidden to visible, so any transitions should769 // be added to the stack to get added to any Offscreen/suspense children770 const instance: OffscreenInstance | null = workInProgress.stateNode;771 if (instance !== null && instance._transitions != null) {772 transitions = Array.from(instance._transitions);773 }774 }775776 pushTransition(workInProgress, prevCachePool, transitions);777778 // Push the lanes that were skipped when we bailed out.779 pushHiddenContext(workInProgress, prevState);780 reuseSuspenseHandlerOnStack(workInProgress);781782 // Since we're not hidden anymore, reset the state783 workInProgress.memoizedState = null;784 } else {785 // We weren't previously hidden, and we still aren't, so there's nothing786 // special to do. Need to push to the stack regardless, though, to avoid787 // a push/pop misalignment.788789 // If the render that spawned this one accessed the cache pool, resume790 // using the same cache. Unless the parent changed, since that means791 // there was a refresh.792 if (current !== null) {793 pushTransition(workInProgress, null, null);794 }795796 // We're about to bail out, but we need to push this to the stack anyway797 // to avoid a push/pop misalignment.798 reuseHiddenContextOnStack(workInProgress);799 reuseSuspenseHandlerOnStack(workInProgress);800 }801 }802803 reconcileChildren(current, workInProgress, nextChildren, renderLanes);804 return workInProgress.child;805}806807function bailoutOffscreenComponent(808 current: Fiber | null,809 workInProgress: Fiber,810): Fiber | null {811 if (812 (current === null || current.tag !== OffscreenComponent) &&813 workInProgress.stateNode === null814 ) {815 const primaryChildInstance: OffscreenInstance = {816 _visibility: OffscreenVisible,817 _pendingMarkers: null,818 _retryCache: null,819 _transitions: null,820 };821 workInProgress.stateNode = primaryChildInstance;822 }823824 return workInProgress.sibling;825}826827function deferHiddenOffscreenComponent(828 current: Fiber | null,829 workInProgress: Fiber,830 nextBaseLanes: Lanes,831 renderLanes: Lanes,832 remainingChildLanes: Lanes,833) {834 const nextState: OffscreenState = {835 baseLanes: nextBaseLanes,836 // Save the cache pool so we can resume later.837 cachePool: getOffscreenDeferredCache(),838 };839 workInProgress.memoizedState = nextState;840 // push the cache pool even though we're going to bail out841 // because otherwise there'd be a context mismatch842 if (current !== null) {843 pushTransition(workInProgress, null, null);844 }845846 // We're about to bail out, but we need to push this to the stack anyway847 // to avoid a push/pop misalignment.848 reuseHiddenContextOnStack(workInProgress);849850 pushOffscreenSuspenseHandler(workInProgress);851852 if (current !== null) {853 // Since this tree will resume rendering in a separate render, we need854 // to propagate parent contexts now so we don't lose track of which855 // ones changed.856 propagateParentContextChangesToDeferredTree(857 current,858 workInProgress,859 renderLanes,860 );861 }862863 // We override the remaining child lanes to be the subset that we computed864 // on the outside. We need to do this after propagating the context865 // because propagateParentContextChangesToDeferredTree may schedule866 // work which bubbles all the way up to the root and updates our child lanes.867 // We want to dismiss that since we're not going to work on it yet.868 workInProgress.childLanes = remainingChildLanes;869870 return null;871}872873function updateLegacyHiddenComponent(874 current: null | Fiber,875 workInProgress: Fiber,876 renderLanes: Lanes,877) {878 const nextProps: LegacyHiddenProps = workInProgress.pendingProps;879 // Note: These happen to have identical begin phases, for now. We shouldn't hold880 // ourselves to this constraint, though. If the behavior diverges, we should881 // fork the function.882 // This just works today because it has the same Props.883 return updateOffscreenComponent(884 current,885 workInProgress,886 renderLanes,887 nextProps,888 );889}890891function mountActivityChildren(892 workInProgress: Fiber,893 nextProps: ActivityProps,894 renderLanes: Lanes,895) {896 if (__DEV__) {897 const hiddenProp = (nextProps as any).hidden;898 if (hiddenProp !== undefined) {899 console.error(900 '<Activity> doesn\'t accept a hidden prop. Use mode="hidden" instead.\n' +901 '- <Activity %s>\n' +902 '+ <Activity %s>',903 hiddenProp === true904 ? 'hidden'905 : hiddenProp === false906 ? 'hidden={false}'907 : 'hidden={...}',908 hiddenProp ? 'mode="hidden"' : 'mode="visible"',909 );910 }911 }912 const nextChildren = nextProps.children;913 const nextMode = nextProps.mode;914 const mode = workInProgress.mode;915 const offscreenChildProps: OffscreenProps = {916 mode: nextMode,917 children: nextChildren,918 };919 const primaryChildFragment = mountWorkInProgressOffscreenFiber(920 offscreenChildProps,921 mode,922 renderLanes,923 );924 primaryChildFragment.ref = workInProgress.ref;925 workInProgress.child = primaryChildFragment;926 primaryChildFragment.return = workInProgress;927 return primaryChildFragment;928}929930function retryActivityComponentWithoutHydrating(931 current: Fiber,932 workInProgress: Fiber,933 renderLanes: Lanes,934) {935 // Falling back to client rendering. Because this has performance936 // implications, it's considered a recoverable error, even though the user937 // likely won't observe anything wrong with the UI.938939 // This will add the old fiber to the deletion list940 reconcileChildFibers(workInProgress, current.child, null, renderLanes);941942 // We're now not suspended nor dehydrated.943 const nextProps: ActivityProps = workInProgress.pendingProps;944 const primaryChildFragment = mountActivityChildren(945 workInProgress,946 nextProps,947 renderLanes,948 );949 // Needs a placement effect because the parent (the Activity boundary) already950 // mounted but this is a new fiber.951 primaryChildFragment.flags |= Placement;952953 // If we're not going to hydrate we can't leave it dehydrated if something954 // suspends. In that case we want that to bubble to the nearest parent boundary955 // so we need to pop our own handler that we just pushed.956 popSuspenseHandler(workInProgress);957958 workInProgress.memoizedState = null;959960 return primaryChildFragment;961}962963function mountDehydratedActivityComponent(964 workInProgress: Fiber,965 activityInstance: ActivityInstance,966 renderLanes: Lanes,967): null | Fiber {968 // During the first pass, we'll bail out and not drill into the children.969 // Instead, we'll leave the content in place and try to hydrate it later.970 // We'll continue hydrating the rest at offscreen priority since we'll already971 // be showing the right content coming from the server, it is no rush.972 workInProgress.lanes = laneToLanes(OffscreenLane);973 return null;974}975976function updateDehydratedActivityComponent(977 current: Fiber,978 workInProgress: Fiber,979 didSuspend: boolean,980 nextProps: ActivityProps,981 activityInstance: ActivityInstance,982 activityState: ActivityState,983 renderLanes: Lanes,984): null | Fiber {985 // We'll handle suspending since if something suspends we can just leave986 // it dehydrated. We push early and then pop if we enter non-dehydrated attempts.987 pushDehydratedActivitySuspenseHandler(workInProgress);988 if (!didSuspend) {989 // This is the first render pass. Attempt to hydrate.990991 // We should never be hydrating at this point because it is the first pass,992 // but after we've already committed once.993 warnIfHydrating();994995 if (includesSomeLane(renderLanes, OffscreenLane as Lane)) {996 // If we're rendering Offscreen and we're entering the activity then it's possible997 // that the only reason we rendered was because this boundary left work. Provide998 // it as a cause if another one doesn't already exist.999 markRenderDerivedCause(workInProgress);1000 }10011002 if (1003 // TODO: Factoring is a little weird, since we check this right below, too.1004 !didReceiveUpdate1005 ) {1006 // We need to check if any children have context before we decide to bail1007 // out, so propagate the changes now.1008 lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);1009 }10101011 // We use lanes to indicate that a child might depend on context, so if1012 // any context has changed, we need to treat is as if the input might have changed.1013 const hasContextChanged = includesSomeLane(renderLanes, current.childLanes);1014 if (didReceiveUpdate || hasContextChanged) {1015 // This boundary has changed since the first render. This means that we are now unable to1016 // hydrate it. We might still be able to hydrate it using a higher priority lane.1017 const root = getWorkInProgressRoot();1018 if (root !== null) {1019 const attemptHydrationAtLane = getBumpedLaneForHydration(1020 root,1021 renderLanes,1022 );1023 if (1024 attemptHydrationAtLane !== NoLane &&1025 attemptHydrationAtLane !== activityState.retryLane1026 ) {1027 // Intentionally mutating since this render will get interrupted. This1028 // is one of the very rare times where we mutate the current tree1029 // during the render phase.1030 activityState.retryLane = attemptHydrationAtLane;1031 enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);1032 scheduleUpdateOnFiber(root, current, attemptHydrationAtLane);10331034 // Throw a special object that signals to the work loop that it should1035 // interrupt the current render.1036 //1037 // Because we're inside a React-only execution stack, we don't1038 // strictly need to throw here — we could instead modify some internal1039 // work loop state. But using an exception means we don't need to1040 // check for this case on every iteration of the work loop. So doing1041 // it this way moves the check out of the fast path.1042 throw SelectiveHydrationException;1043 } else {1044 // We have already tried to ping at a higher priority than we're rendering with1045 // so if we got here, we must have failed to hydrate at those levels. We must1046 // now give up. Instead, we're going to delete the whole subtree and instead inject1047 // a new real Activity boundary to take its place. This might suspend for a while1048 // and if it does we might still have an opportunity to hydrate before this pass1049 // commits.1050 }1051 }10521053 // If we did not selectively hydrate, we'll continue rendering without1054 // hydrating. Mark this tree as suspended to prevent it from committing1055 // outside a transition.1056 //1057 // This path should only happen if the hydration lane already suspended.1058 renderDidSuspendDelayIfPossible();1059 return retryActivityComponentWithoutHydrating(1060 current,1061 workInProgress,1062 renderLanes,1063 );1064 } else {1065 // This is the first attempt.10661067 reenterHydrationStateFromDehydratedActivityInstance(1068 workInProgress,1069 activityInstance,1070 activityState.treeContext,1071 );10721073 const primaryChildFragment = mountActivityChildren(1074 workInProgress,1075 nextProps,1076 renderLanes,1077 );1078 // Mark the children as hydrating. This is a fast path to know whether this1079 // tree is part of a hydrating tree. This is used to determine if a child1080 // node has fully mounted yet, and for scheduling event replaying.1081 // Conceptually this is similar to Placement in that a new subtree is1082 // inserted into the React tree here. It just happens to not need DOM1083 // mutations because it already exists.1084 // We should still treat it as a newly inserted Fiber to double invoke Strict Effects.1085 primaryChildFragment.flags |= Hydrating | PlacementDEV;1086 return primaryChildFragment;1087 }1088 } else {1089 // This is the second render pass. We already attempted to hydrated, but1090 // something either suspended or errored.10911092 if (workInProgress.flags & ForceClientRender) {1093 // Something errored during hydration. Try again without hydrating.1094 // The error should've already been logged in throwException.1095 workInProgress.flags &= ~ForceClientRender;1096 return retryActivityComponentWithoutHydrating(1097 current,1098 workInProgress,1099 renderLanes,1100 );1101 } else if (1102 (workInProgress.memoizedState as null | ActivityState) !== null1103 ) {1104 // Something suspended and we should still be in dehydrated mode.1105 // Leave the existing child in place.11061107 workInProgress.child = current.child;1108 // The dehydrated completion pass expects this flag to be there1109 // but the normal offscreen pass doesn't.1110 workInProgress.flags |= DidCapture;1111 return null;1112 } else {1113 // We called retryActivityComponentWithoutHydrating and tried client rendering1114 // but now we suspended again. We should never arrive here because we should1115 // not have pushed a suspense handler during that second pass and it should1116 // instead have suspended above.1117 throw new Error(1118 'Client rendering an Activity suspended it again. This is a bug in React.',1119 );1120 }1121 }1122}11231124function updateActivityComponent(1125 current: null | Fiber,1126 workInProgress: Fiber,1127 renderLanes: Lanes,1128) {1129 const nextProps: ActivityProps = workInProgress.pendingProps;11301131 // Check if the first pass suspended.1132 const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;1133 workInProgress.flags &= ~DidCapture;11341135 if (current === null) {1136 // Initial mount11371138 // Special path for hydration1139 // If we're currently hydrating, try to hydrate this boundary.1140 // Hidden Activity boundaries are not emitted on the server.1141 if (getIsHydrating()) {1142 if (nextProps.mode === 'hidden') {1143 // SSR doesn't render hidden Activity so it shouldn't hydrate,1144 // even at offscreen lane. Defer to a client rendered offscreen lane.1145 const primaryChildFragment = mountActivityChildren(1146 workInProgress,1147 nextProps,1148 renderLanes,1149 );1150 workInProgress.lanes = laneToLanes(OffscreenLane);1151 return bailoutOffscreenComponent(null, primaryChildFragment);1152 } else {1153 // We must push the suspense handler context *before* attempting to1154 // hydrate, to avoid a mismatch in case it errors.1155 pushDehydratedActivitySuspenseHandler(workInProgress);1156 const dehydrated: ActivityInstance =1157 claimNextHydratableActivityInstance(workInProgress);1158 return mountDehydratedActivityComponent(1159 workInProgress,1160 dehydrated,1161 renderLanes,1162 );1163 }1164 }11651166 return mountActivityChildren(workInProgress, nextProps, renderLanes);1167 } else {1168 // This is an update.11691170 // Special path for hydration1171 const prevState: null | ActivityState = current.memoizedState;11721173 if (prevState !== null) {1174 const dehydrated = prevState.dehydrated;1175 return updateDehydratedActivityComponent(1176 current,1177 workInProgress,1178 didSuspend,1179 nextProps,1180 dehydrated,1181 prevState,1182 renderLanes,1183 );1184 }11851186 const currentChild: Fiber = current.child as any;11871188 const nextChildren = nextProps.children;1189 const nextMode = nextProps.mode;1190 const offscreenChildProps: OffscreenProps = {1191 mode: nextMode,1192 children: nextChildren,1193 };11941195 if (1196 includesSomeLane(renderLanes, OffscreenLane as Lane) &&1197 includesSomeLane(renderLanes, current.lanes)1198 ) {1199 // If we're rendering Offscreen and we're entering the activity then it's possible1200 // that the only reason we rendered was because this boundary left work. Provide1201 // it as a cause if another one doesn't already exist.1202 markRenderDerivedCause(workInProgress);1203 }12041205 const primaryChildFragment = updateWorkInProgressOffscreenFiber(1206 currentChild,1207 offscreenChildProps,1208 );12091210 primaryChildFragment.ref = workInProgress.ref;1211 workInProgress.child = primaryChildFragment;1212 primaryChildFragment.return = workInProgress;1213 return primaryChildFragment;1214 }1215}12161217function updateCacheComponent(1218 current: Fiber | null,1219 workInProgress: Fiber,1220 renderLanes: Lanes,1221) {1222 prepareToReadContext(workInProgress, renderLanes);1223 const parentCache = readContext(CacheContext);12241225 if (current === null) {1226 // Initial mount. Request a fresh cache from the pool.1227 const freshCache = requestCacheFromPool(renderLanes);1228 const initialState: CacheComponentState = {1229 parent: parentCache,1230 cache: freshCache,1231 };1232 workInProgress.memoizedState = initialState;1233 initializeUpdateQueue(workInProgress);1234 pushCacheProvider(workInProgress, freshCache);1235 } else {1236 // Check for updates1237 if (includesSomeLane(current.lanes, renderLanes)) {1238 cloneUpdateQueue(current, workInProgress);1239 processUpdateQueue(workInProgress, null, null, renderLanes);1240 suspendIfUpdateReadFromEntangledAsyncAction();1241 }1242 const prevState: CacheComponentState = current.memoizedState;1243 const nextState: CacheComponentState = workInProgress.memoizedState;12441245 // Compare the new parent cache to the previous to see detect there was1246 // a refresh.1247 if (prevState.parent !== parentCache) {1248 // Refresh in parent. Update the parent.1249 const derivedState: CacheComponentState = {1250 parent: parentCache,1251 cache: parentCache,1252 };12531254 // Copied from getDerivedStateFromProps implementation. Once the update1255 // queue is empty, persist the derived state onto the base state.1256 workInProgress.memoizedState = derivedState;1257 if (workInProgress.lanes === NoLanes) {1258 const updateQueue: UpdateQueue<any> = workInProgress.updateQueue as any;1259 workInProgress.memoizedState = updateQueue.baseState = derivedState;1260 }12611262 pushCacheProvider(workInProgress, parentCache);1263 // No need to propagate a context change because the refreshed parent1264 // already did.1265 } else {1266 // The parent didn't refresh. Now check if this cache did.1267 const nextCache = nextState.cache;1268 pushCacheProvider(workInProgress, nextCache);1269 if (nextCache !== prevState.cache) {1270 // This cache refreshed. Propagate a context change.1271 propagateContextChange(workInProgress, CacheContext, renderLanes);1272 }1273 }1274 }12751276 const nextProps: CacheProps = workInProgress.pendingProps;12771278 const nextChildren = nextProps.children;1279 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1280 return workInProgress.child;1281}12821283// This should only be called if the name changes1284function updateTracingMarkerComponent(1285 current: Fiber | null,1286 workInProgress: Fiber,1287 renderLanes: Lanes,1288) {1289 if (!enableTransitionTracing) {1290 return null;1291 }12921293 const nextProps: TracingMarkerProps = workInProgress.pendingProps;12941295 // TODO: (luna) Only update the tracing marker if it's newly rendered or it's name changed.1296 // A tracing marker is only associated with the transitions that rendered1297 // or updated it, so we can create a new set of transitions each time1298 if (current === null) {1299 const currentTransitions = getPendingTransitions();1300 if (currentTransitions !== null) {1301 const markerInstance: TracingMarkerInstance = {1302 tag: TransitionTracingMarker,1303 transitions: new Set(currentTransitions),1304 pendingBoundaries: null,1305 name: nextProps.name,1306 aborts: null,1307 };1308 workInProgress.stateNode = markerInstance;13091310 // We call the marker complete callback when all child suspense boundaries resolve.1311 // We do this in the commit phase on Offscreen. If the marker has no child suspense1312 // boundaries, we need to schedule a passive effect to make sure we call the marker1313 // complete callback.1314 workInProgress.flags |= Passive;1315 }1316 } else {1317 if (__DEV__) {1318 if (current.memoizedProps.name !== nextProps.name) {1319 console.error(1320 'Changing the name of a tracing marker after mount is not supported. ' +1321 'To remount the tracing marker, pass it a new key.',1322 );1323 }1324 }1325 }13261327 const instance: TracingMarkerInstance | null = workInProgress.stateNode;1328 if (instance !== null) {1329 pushMarkerInstance(workInProgress, instance);1330 }1331 const nextChildren = nextProps.children;1332 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1333 return workInProgress.child;1334}13351336function updateFragment(1337 current: Fiber | null,1338 workInProgress: Fiber,1339 renderLanes: Lanes,1340) {1341 const nextChildren = workInProgress.pendingProps;1342 if (enableFragmentRefs) {1343 markRef(current, workInProgress);1344 }1345 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1346 return workInProgress.child;1347}13481349function updateMode(1350 current: Fiber | null,1351 workInProgress: Fiber,1352 renderLanes: Lanes,1353) {1354 const nextChildren = workInProgress.pendingProps.children;1355 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1356 return workInProgress.child;1357}13581359function updateProfiler(1360 current: Fiber | null,1361 workInProgress: Fiber,1362 renderLanes: Lanes,1363) {1364 if (enableProfilerTimer) {1365 workInProgress.flags |= Update;13661367 if (enableProfilerCommitHooks) {1368 // Schedule a passive effect for this Profiler to call onPostCommit hooks.1369 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,1370 // because the effect is also where times bubble to parent Profilers.1371 workInProgress.flags |= Passive;1372 // Reset effect durations for the next eventual effect phase.1373 // These are reset during render to allow the DevTools commit hook a chance to read them,1374 const stateNode = workInProgress.stateNode;1375 stateNode.effectDuration = -0;1376 stateNode.passiveEffectDuration = -0;1377 }1378 }1379 const nextProps: ProfilerProps = workInProgress.pendingProps;1380 const nextChildren = nextProps.children;1381 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1382 return workInProgress.child;1383}13841385function markRef(current: Fiber | null, workInProgress: Fiber) {1386 // TODO: Check props.ref instead of fiber.ref when enableRefAsProp is on.1387 const ref = workInProgress.ref;1388 if (ref === null) {1389 if (current !== null && current.ref !== null) {1390 // Schedule a Ref effect1391 workInProgress.flags |= Ref | RefStatic;1392 }1393 } else {1394 if (typeof ref !== 'function' && typeof ref !== 'object') {1395 throw new Error(1396 'Expected ref to be a function, an object returned by React.createRef(), or undefined/null.',1397 );1398 }1399 if (current === null || current.ref !== ref) {1400 // Schedule a Ref effect1401 workInProgress.flags |= Ref | RefStatic;1402 }1403 }1404}14051406function mountIncompleteFunctionComponent(1407 _current: null | Fiber,1408 workInProgress: Fiber,1409 Component: any,1410 nextProps: any,1411 renderLanes: Lanes,1412) {1413 resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);14141415 workInProgress.tag = FunctionComponent;14161417 return updateFunctionComponent(1418 null,1419 workInProgress,1420 Component,1421 nextProps,1422 renderLanes,1423 );1424}14251426function updateFunctionComponent(1427 current: null | Fiber,1428 workInProgress: Fiber,1429 Component: any,1430 nextProps: any,1431 renderLanes: Lanes,1432) {1433 if (__DEV__) {1434 if (1435 Component.prototype &&1436 typeof Component.prototype.render === 'function'1437 ) {1438 const componentName = getComponentNameFromType(Component) || 'Unknown';14391440 if (!didWarnAboutBadClass[componentName]) {1441 console.error(1442 "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +1443 'This is likely to cause errors. Change %s to extend React.Component instead.',1444 componentName,1445 componentName,1446 );1447 didWarnAboutBadClass[componentName] = true;1448 }1449 }14501451 if (workInProgress.mode & StrictLegacyMode) {1452 ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);1453 }14541455 if (current === null) {1456 // Some validations were previously done in mountIndeterminateComponent however and are now run1457 // in updateFuntionComponent but only on mount1458 validateFunctionComponentInDev(workInProgress, workInProgress.type);14591460 if (Component.contextTypes) {1461 const componentName = getComponentNameFromType(Component) || 'Unknown';14621463 if (!didWarnAboutContextTypes[componentName]) {1464 didWarnAboutContextTypes[componentName] = true;1465 if (disableLegacyContext) {1466 console.error(1467 '%s uses the legacy contextTypes API which was removed in React 19. ' +1468 'Use React.createContext() with React.useContext() instead. ' +1469 '(https://react.dev/link/legacy-context)',1470 componentName,1471 );1472 } else {1473 console.error(1474 '%s uses the legacy contextTypes API which will be removed soon. ' +1475 'Use React.createContext() with React.useContext() instead. ' +1476 '(https://react.dev/link/legacy-context)',1477 componentName,1478 );1479 }1480 }1481 }1482 }1483 }14841485 let context;1486 if (!disableLegacyContext && !disableLegacyContextForFunctionComponents) {1487 const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);1488 context = getMaskedContext(workInProgress, unmaskedContext);1489 }14901491 let nextChildren;1492 let hasId;1493 prepareToReadContext(workInProgress, renderLanes);1494 if (enableSchedulingProfiler) {1495 markComponentRenderStarted(workInProgress);1496 }1497 if (__DEV__) {1498 nextChildren = renderWithHooks(1499 current,1500 workInProgress,1501 Component,1502 nextProps,1503 context,1504 renderLanes,1505 );1506 hasId = checkDidRenderIdHook();1507 } else {1508 nextChildren = renderWithHooks(1509 current,1510 workInProgress,1511 Component,1512 nextProps,1513 context,1514 renderLanes,1515 );1516 hasId = checkDidRenderIdHook();1517 }1518 if (enableSchedulingProfiler) {1519 markComponentRenderStopped();1520 }15211522 if (current !== null && !didReceiveUpdate) {1523 bailoutHooks(current, workInProgress, renderLanes);1524 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1525 }15261527 if (getIsHydrating() && hasId) {1528 pushMaterializedTreeId(workInProgress);1529 }15301531 // React DevTools reads this flag.1532 workInProgress.flags |= PerformedWork;1533 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1534 return workInProgress.child;1535}15361537export function replayFunctionComponent(1538 current: Fiber | null,1539 workInProgress: Fiber,1540 nextProps: any,1541 Component: any,1542 secondArg: any,1543 renderLanes: Lanes,1544): Fiber | null {1545 // This function is used to replay a component that previously suspended,1546 // after its data resolves. It's a simplified version of1547 // updateFunctionComponent that reuses the hooks from the previous attempt.15481549 prepareToReadContext(workInProgress, renderLanes);1550 if (enableSchedulingProfiler) {1551 markComponentRenderStarted(workInProgress);1552 }1553 const nextChildren = replaySuspendedComponentWithHooks(1554 current,1555 workInProgress,1556 Component,1557 nextProps,1558 secondArg,1559 );1560 const hasId = checkDidRenderIdHook();1561 if (enableSchedulingProfiler) {1562 markComponentRenderStopped();1563 }15641565 if (current !== null && !didReceiveUpdate) {1566 bailoutHooks(current, workInProgress, renderLanes);1567 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1568 }15691570 if (getIsHydrating() && hasId) {1571 pushMaterializedTreeId(workInProgress);1572 }15731574 // React DevTools reads this flag.1575 workInProgress.flags |= PerformedWork;1576 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1577 return workInProgress.child;1578}15791580function updateClassComponent(1581 current: Fiber | null,1582 workInProgress: Fiber,1583 Component: any,1584 nextProps: any,1585 renderLanes: Lanes,1586) {1587 if (__DEV__) {1588 // This is used by DevTools to force a boundary to error.1589 switch (shouldError(workInProgress)) {1590 case false: {1591 // We previously simulated an error on this boundary1592 // so the instance must have been constructed in a previous1593 // commit.1594 const instance = workInProgress.stateNode;1595 const ctor = workInProgress.type;1596 // TODO This way of resetting the error boundary state is a hack.1597 // Is there a better way to do this?1598 const tempInstance = new ctor(1599 workInProgress.memoizedProps,1600 instance.context,1601 );1602 const state = tempInstance.state;1603 instance.updater.enqueueSetState(instance, state, null);1604 break;1605 }1606 case true: {1607 workInProgress.flags |= DidCapture;1608 workInProgress.flags |= ShouldCapture;1609 // eslint-disable-next-line react-internal/prod-error-codes1610 const error = new Error('Simulated error coming from DevTools');1611 const lane = pickArbitraryLane(renderLanes);1612 workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);1613 // Schedule the error boundary to re-render using updated state1614 const root: FiberRoot | null = getWorkInProgressRoot();1615 if (root === null) {1616 throw new Error(1617 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',1618 );1619 }1620 const update = createClassErrorUpdate(lane);1621 initializeClassErrorUpdate(1622 update,1623 root,1624 workInProgress,1625 createCapturedValueAtFiber(error, workInProgress),1626 );1627 enqueueCapturedUpdate(workInProgress, update);1628 break;1629 }1630 }1631 }16321633 // Push context providers early to prevent context stack mismatches.1634 // During mounting we don't know the child context yet as the instance doesn't exist.1635 // We will invalidate the child context in finishClassComponent() right after rendering.1636 let hasContext;1637 if (isLegacyContextProvider(Component)) {1638 hasContext = true;1639 pushLegacyContextProvider(workInProgress);1640 } else {1641 hasContext = false;1642 }1643 prepareToReadContext(workInProgress, renderLanes);16441645 const instance = workInProgress.stateNode;1646 let shouldUpdate;1647 if (instance === null) {1648 resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress);16491650 // In the initial pass we might need to construct the instance.1651 constructClassInstance(workInProgress, Component, nextProps);1652 mountClassInstance(workInProgress, Component, nextProps, renderLanes);1653 shouldUpdate = true;1654 } else if (current === null) {1655 // In a resume, we'll already have an instance we can reuse.1656 shouldUpdate = resumeMountClassInstance(1657 workInProgress,1658 Component,1659 nextProps,1660 renderLanes,1661 );1662 } else {1663 shouldUpdate = updateClassInstance(1664 current,1665 workInProgress,1666 Component,1667 nextProps,1668 renderLanes,1669 );1670 }1671 const nextUnitOfWork = finishClassComponent(1672 current,1673 workInProgress,1674 Component,1675 shouldUpdate,1676 hasContext,1677 renderLanes,1678 );1679 if (__DEV__) {1680 const inst = workInProgress.stateNode;1681 if (shouldUpdate && inst.props !== nextProps) {1682 if (!didWarnAboutReassigningProps) {1683 console.error(1684 'It looks like %s is reassigning its own `this.props` while rendering. ' +1685 'This is not supported and can lead to confusing bugs.',1686 getComponentNameFromFiber(workInProgress) || 'a component',1687 );1688 }1689 didWarnAboutReassigningProps = true;1690 }1691 }1692 return nextUnitOfWork;1693}16941695function finishClassComponent(1696 current: Fiber | null,1697 workInProgress: Fiber,1698 Component: any,1699 shouldUpdate: boolean,1700 hasContext: boolean,1701 renderLanes: Lanes,1702) {1703 // Refs should update even if shouldComponentUpdate returns false1704 markRef(current, workInProgress);17051706 const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;17071708 if (!shouldUpdate && !didCaptureError) {1709 // Context providers should defer to sCU for rendering1710 if (hasContext) {1711 invalidateContextProvider(workInProgress, Component, false);1712 }17131714 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1715 }17161717 const instance = workInProgress.stateNode;17181719 // Rerender1720 if (__DEV__) {1721 setCurrentFiber(workInProgress);1722 }1723 let nextChildren;1724 if (1725 didCaptureError &&1726 typeof Component.getDerivedStateFromError !== 'function'1727 ) {1728 // If we captured an error, but getDerivedStateFromError is not defined,1729 // unmount all the children. componentDidCatch will schedule an update to1730 // re-render a fallback. This is temporary until we migrate everyone to1731 // the new API.1732 // TODO: Warn in a future release.1733 nextChildren = null;17341735 if (enableProfilerTimer) {1736 stopProfilerTimerIfRunning(workInProgress);1737 }1738 } else {1739 if (enableSchedulingProfiler) {1740 markComponentRenderStarted(workInProgress);1741 }1742 if (__DEV__) {1743 nextChildren = callRenderInDEV(instance);1744 if (workInProgress.mode & StrictLegacyMode) {1745 setIsStrictModeForDevtools(true);1746 try {1747 callRenderInDEV(instance);1748 } finally {1749 setIsStrictModeForDevtools(false);1750 }1751 }1752 } else {1753 nextChildren = instance.render();1754 }1755 if (enableSchedulingProfiler) {1756 markComponentRenderStopped();1757 }1758 }17591760 // React DevTools reads this flag.1761 workInProgress.flags |= PerformedWork;1762 if (current !== null && didCaptureError) {1763 // If we're recovering from an error, reconcile without reusing any of1764 // the existing children. Conceptually, the normal children and the children1765 // that are shown on error are two different sets, so we shouldn't reuse1766 // normal children even if their identities match.1767 forceUnmountCurrentAndReconcile(1768 current,1769 workInProgress,1770 nextChildren,1771 renderLanes,1772 );1773 } else {1774 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1775 }17761777 // Memoize state using the values we just used to render.1778 // TODO: Restructure so we never read values from the instance.1779 workInProgress.memoizedState = instance.state;17801781 // The context might have changed so we need to recalculate it.1782 if (hasContext) {1783 invalidateContextProvider(workInProgress, Component, true);1784 }17851786 return workInProgress.child;1787}17881789function pushHostRootContext(workInProgress: Fiber) {1790 const root = workInProgress.stateNode as FiberRoot;1791 if (root.pendingContext) {1792 pushTopLevelContextObject(1793 workInProgress,1794 root.pendingContext,1795 root.pendingContext !== root.context,1796 );1797 } else if (root.context) {1798 // Should always be set1799 pushTopLevelContextObject(workInProgress, root.context, false);1800 }1801 pushHostContainer(workInProgress, root.containerInfo);1802}18031804function updateHostRoot(1805 current: null | Fiber,1806 workInProgress: Fiber,1807 renderLanes: Lanes,1808) {1809 pushHostRootContext(workInProgress);18101811 if (current === null) {1812 throw new Error('Should have a current fiber. This is a bug in React.');1813 }18141815 const nextProps = workInProgress.pendingProps;1816 const prevState: RootState = workInProgress.memoizedState;1817 const prevChildren = prevState.element;1818 cloneUpdateQueue(current, workInProgress);1819 processUpdateQueue(workInProgress, nextProps, null, renderLanes);18201821 const nextState: RootState = workInProgress.memoizedState;1822 const root: FiberRoot = workInProgress.stateNode;1823 pushRootTransition(workInProgress, root, renderLanes);18241825 if (enableTransitionTracing) {1826 pushRootMarkerInstance(workInProgress);1827 }18281829 const nextCache: Cache = nextState.cache;1830 pushCacheProvider(workInProgress, nextCache);1831 if (nextCache !== prevState.cache) {1832 // The root cache refreshed.1833 propagateContextChange(workInProgress, CacheContext, renderLanes);1834 }18351836 // This would ideally go inside processUpdateQueue, but because it suspends,1837 // it needs to happen after the `pushCacheProvider` call above to avoid a1838 // context stack mismatch. A bit unfortunate.1839 suspendIfUpdateReadFromEntangledAsyncAction();18401841 // Caution: React DevTools currently depends on this property1842 // being called "element".1843 const nextChildren = nextState.element;1844 // $FlowFixMe[constant-condition]1845 if (supportsHydration && prevState.isDehydrated) {1846 // This is a hydration root whose shell has not yet hydrated. We should1847 // attempt to hydrate.18481849 // Flip isDehydrated to false to indicate that when this render1850 // finishes, the root will no longer be dehydrated.1851 const overrideState: RootState = {1852 element: nextChildren,1853 isDehydrated: false,1854 cache: nextState.cache,1855 };1856 const updateQueue: UpdateQueue<RootState> =1857 workInProgress.updateQueue as any;1858 // `baseState` can always be the last state because the root doesn't1859 // have reducer functions so it doesn't need rebasing.1860 updateQueue.baseState = overrideState;1861 workInProgress.memoizedState = overrideState;18621863 if (workInProgress.flags & ForceClientRender) {1864 // Something errored during a previous attempt to hydrate the shell, so we1865 // forced a client render. We should have a recoverable error already scheduled.1866 return mountHostRootWithoutHydrating(1867 current,1868 workInProgress,1869 nextChildren,1870 renderLanes,1871 );1872 } else if (nextChildren !== prevChildren) {1873 const recoverableError = createCapturedValueAtFiber<mixed>(1874 new Error(1875 'This root received an early update, before anything was able ' +1876 'hydrate. Switched the entire root to client rendering.',1877 ),1878 workInProgress,1879 );1880 queueHydrationError(recoverableError);1881 return mountHostRootWithoutHydrating(1882 current,1883 workInProgress,1884 nextChildren,1885 renderLanes,1886 );1887 } else {1888 // The outermost shell has not hydrated yet. Start hydrating.1889 enterHydrationState(workInProgress);18901891 const child = mountChildFibers(1892 workInProgress,1893 null,1894 nextChildren,1895 renderLanes,1896 );1897 workInProgress.child = child;18981899 let node = child;1900 while (node) {1901 // Mark each child as hydrating. This is a fast path to know whether this1902 // tree is part of a hydrating tree. This is used to determine if a child1903 // node has fully mounted yet, and for scheduling event replaying.1904 // Conceptually this is similar to Placement in that a new subtree is1905 // inserted into the React tree here. It just happens to not need DOM1906 // mutations because it already exists.1907 // We should still treat it as a newly inserted Fiber to double invoke Strict Effects.1908 node.flags = (node.flags & ~Placement) | Hydrating | PlacementDEV;1909 node = node.sibling;1910 }1911 }1912 } else {1913 // Root is not dehydrated. Either this is a client-only root, or it1914 // already hydrated.1915 resetHydrationState();1916 if (nextChildren === prevChildren) {1917 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1918 }1919 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1920 }1921 return workInProgress.child;1922}19231924function mountHostRootWithoutHydrating(1925 current: Fiber,1926 workInProgress: Fiber,1927 nextChildren: ReactNodeList,1928 renderLanes: Lanes,1929) {1930 // Revert to client rendering.1931 resetHydrationState();19321933 workInProgress.flags |= ForceClientRender;19341935 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1936 return workInProgress.child;1937}19381939function updateHostComponent(1940 current: Fiber | null,1941 workInProgress: Fiber,1942 renderLanes: Lanes,1943) {1944 if (current === null) {1945 tryToClaimNextHydratableInstance(workInProgress);1946 }19471948 pushHostContext(workInProgress);19491950 const type = workInProgress.type;1951 const nextProps = workInProgress.pendingProps;1952 const prevProps = current !== null ? current.memoizedProps : null;19531954 let nextChildren = nextProps.children;1955 const isDirectTextChild = shouldSetTextContent(type, nextProps);19561957 if (isDirectTextChild) {1958 // We special case a direct text child of a host node. This is a common1959 // case. We won't handle it as a reified child. We will instead handle1960 // this in the host environment that also has access to this prop. That1961 // avoids allocating another HostText fiber and traversing it.1962 nextChildren = null;1963 } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {1964 // If we're switching from a direct text child to a normal child, or to1965 // empty, we need to schedule the text content to be reset.1966 workInProgress.flags |= ContentReset;1967 }19681969 const memoizedState = workInProgress.memoizedState;1970 if (memoizedState !== null) {1971 // This fiber has been upgraded to a stateful component. The only way1972 // happens currently is for form actions. We use hooks to track the1973 // pending and error state of the form.1974 //1975 // Once a fiber is upgraded to be stateful, it remains stateful for the1976 // rest of its lifetime.1977 const newState = renderTransitionAwareHostComponentWithHooks(1978 current,1979 workInProgress,1980 renderLanes,1981 );19821983 // If the transition state changed, propagate the change to all the1984 // descendents. We use Context as an implementation detail for this.1985 //1986 // We need to update it here because1987 // pushHostContext gets called before we process the state hook, to avoid1988 // a state mismatch in the event that something suspends.1989 //1990 // NOTE: This assumes that there cannot be nested transition providers,1991 // because the only renderer that implements this feature is React DOM,1992 // and forms cannot be nested. If we did support nested providers, then1993 // we would need to push a context value even for host fibers that1994 // haven't been upgraded yet.1995 // $FlowFixMe[constant-condition]1996 if (isPrimaryRenderer) {1997 HostTransitionContext._currentValue = newState;1998 } else {1999 HostTransitionContext._currentValue2 = newState;2000 }
Findings
✓ No findings reported for this file.