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 {REACT_RECOVERABLE_DIGEST} from 'shared/ReactRecoverable';135import {setCurrentFiber} from './ReactCurrentFiber';136import {resolveTypeForHotReloading} from './ReactFiberHotReloading';137138import {139 mountChildFibers,140 reconcileChildFibers,141 cloneChildFibers,142 validateSuspenseListChildren,143} from './ReactChildFiber';144import {145 processUpdateQueue,146 cloneUpdateQueue,147 initializeUpdateQueue,148 enqueueCapturedUpdate,149 suspendIfUpdateReadFromEntangledAsyncAction,150} from './ReactFiberClassUpdateQueue';151import {152 NoLane,153 NoLanes,154 OffscreenLane,155 DefaultLane,156 SomeRetryLane,157 includesSomeLane,158 includesOnlyRetries,159 laneToLanes,160 removeLanes,161 mergeLanes,162 getBumpedLaneForHydration,163 pickArbitraryLane,164} from './ReactFiberLane';165import {166 ConcurrentMode,167 NoMode,168 ProfileMode,169 StrictLegacyMode,170} from './ReactTypeOfMode';171import {172 shouldSetTextContent,173 isSuspenseInstancePending,174 isSuspenseInstanceFallback,175 getSuspenseInstanceFallbackErrorDetails,176 supportsHydration,177 supportsResources,178 supportsSingletons,179 isPrimaryRenderer,180 getResource,181 createHoistableInstance,182 HostTransitionContext,183} from './ReactFiberConfig';184import type {ActivityInstance, SuspenseInstance} from './ReactFiberConfig';185import {shouldError, shouldSuspend} from './ReactFiberReconciler';186import {187 pushHostContext,188 pushHostContainer,189 getRootHostContainer,190} from './ReactFiberHostContext';191import {192 suspenseStackCursor,193 pushSuspenseListContext,194 ForceSuspenseFallback,195 hasSuspenseListContext,196 setDefaultShallowSuspenseListContext,197 setShallowSuspenseListContext,198 pushPrimaryTreeSuspenseHandler,199 pushFallbackTreeSuspenseHandler,200 pushDehydratedActivitySuspenseHandler,201 pushOffscreenSuspenseHandler,202 reuseSuspenseHandlerOnStack,203 popSuspenseHandler,204} from './ReactFiberSuspenseContext';205import {206 pushHiddenContext,207 reuseHiddenContextOnStack,208 isCurrentTreeHidden,209} from './ReactFiberHiddenContext';210import {findFirstSuspended} from './ReactFiberSuspenseComponent';211import {212 pushProvider,213 propagateContextChange,214 lazilyPropagateParentContextChanges,215 propagateParentContextChangesToDeferredTree,216 checkIfContextChanged,217 readContext,218 prepareToReadContext,219 scheduleContextWorkOnParentPath,220} from './ReactFiberNewContext';221import {222 renderWithHooks,223 checkDidRenderIdHook,224 bailoutHooks,225 replaySuspendedComponentWithHooks,226 renderTransitionAwareHostComponentWithHooks,227} from './ReactFiberHooks';228import {stopProfilerTimerIfRunning} from './ReactProfilerTimer';229import {230 getMaskedContext,231 getUnmaskedContext,232 hasContextChanged as hasLegacyContextChanged,233 pushContextProvider as pushLegacyContextProvider,234 isContextProvider as isLegacyContextProvider,235 pushTopLevelContextObject,236 invalidateContextProvider,237} from './ReactFiberLegacyContext';238import {239 getIsHydrating,240 enterHydrationState,241 reenterHydrationStateFromDehydratedActivityInstance,242 reenterHydrationStateFromDehydratedSuspenseInstance,243 resetHydrationState,244 claimHydratableSingleton,245 tryToClaimNextHydratableInstance,246 tryToClaimNextHydratableTextInstance,247 claimNextHydratableActivityInstance,248 claimNextHydratableSuspenseInstance,249 warnIfHydrating,250 queueHydrationError,251} from './ReactFiberHydrationContext';252import {253 constructClassInstance,254 mountClassInstance,255 resumeMountClassInstance,256 updateClassInstance,257 resolveClassComponentProps,258} from './ReactFiberClassComponent';259import {260 createFiberFromTypeAndProps,261 createFiberFromFragment,262 createFiberFromOffscreen,263 createWorkInProgress,264 isSimpleFunctionComponent,265 isFunctionClassComponent,266} from './ReactFiber';267import {268 scheduleUpdateOnFiber,269 renderDidSuspendDelayIfPossible,270 markSkippedUpdateLanes,271 markRenderDerivedCause,272 getWorkInProgressRoot,273 peekDeferredLane,274} from './ReactFiberWorkLoop';275import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';276import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent';277import {278 createCapturedValueFromError,279 createCapturedValueAtFiber,280} from './ReactCapturedValue';281import {OffscreenVisible} from './ReactFiberOffscreenComponent';282import {283 createClassErrorUpdate,284 initializeClassErrorUpdate,285} from './ReactFiberThrow';286import {287 getForksAtLevel,288 isForkedChild,289 pushTreeId,290 pushMaterializedTreeId,291} from './ReactFiberTreeContext';292import {293 requestCacheFromPool,294 pushRootTransition,295 getSuspendedCache,296 pushTransition,297 getOffscreenDeferredCache,298 getPendingTransitions,299} from './ReactFiberTransition';300import {301 getMarkerInstances,302 pushMarkerInstance,303 pushRootMarkerInstance,304 TransitionTracingMarker,305} from './ReactFiberTracingMarkerComponent';306import {callComponentInDEV, callRenderInDEV} from './ReactFiberCallUserSpace';307import {resolveLazy} from './ReactFiberThenable';308309// A special exception that's used to unwind the stack when an update flows310// into a dehydrated boundary.311export const SelectiveHydrationException: mixed = new Error(312 "This is not a real error. It's an implementation detail of React's " +313 "selective hydration feature. If this leaks into userspace, it's a bug in " +314 'React. Please file an issue.',315);316317let didReceiveUpdate: boolean = false;318319let didWarnAboutBadClass;320let didWarnAboutContextTypeOnFunctionComponent;321let didWarnAboutContextTypes;322let didWarnAboutGetDerivedStateOnFunctionComponent;323export let didWarnAboutReassigningProps: boolean;324let didWarnAboutRevealOrder;325let didWarnAboutTailOptions;326let didWarnAboutClassNameOnViewTransition;327328if (__DEV__) {329 didWarnAboutBadClass = {} as {[string]: boolean};330 didWarnAboutContextTypeOnFunctionComponent = {} as {[string]: boolean};331 didWarnAboutContextTypes = {} as {[string]: boolean};332 didWarnAboutGetDerivedStateOnFunctionComponent = {} as {[string]: boolean};333 didWarnAboutReassigningProps = false;334 didWarnAboutRevealOrder = {} as {[string]: boolean};335 didWarnAboutTailOptions = {} as {[string]: boolean};336 didWarnAboutClassNameOnViewTransition = {} as {[string]: boolean};337}338339export function reconcileChildren(340 current: Fiber | null,341 workInProgress: Fiber,342 nextChildren: any,343 renderLanes: Lanes,344) {345 if (current === null) {346 // If this is a fresh new component that hasn't been rendered yet, we347 // won't update its child set by applying minimal side-effects. Instead,348 // we will add them all to the child before it gets rendered. That means349 // we can optimize this reconciliation pass by not tracking side-effects.350 workInProgress.child = mountChildFibers(351 workInProgress,352 null,353 nextChildren,354 renderLanes,355 );356 } else {357 // If the current child is the same as the work in progress, it means that358 // we haven't yet started any work on these children. Therefore, we use359 // the clone algorithm to create a copy of all the current children.360361 // If we had any progressed work already, that is invalid at this point so362 // let's throw it out.363 workInProgress.child = reconcileChildFibers(364 workInProgress,365 current.child,366 nextChildren,367 renderLanes,368 );369 }370}371372function forceUnmountCurrentAndReconcile(373 current: Fiber,374 workInProgress: Fiber,375 nextChildren: any,376 renderLanes: Lanes,377) {378 // This function is fork of reconcileChildren. It's used in cases where we379 // want to reconcile without matching against the existing set. This has the380 // effect of all current children being unmounted; even if the type and key381 // are the same, the old child is unmounted and a new child is created.382 //383 // To do this, we're going to go through the reconcile algorithm twice. In384 // the first pass, we schedule a deletion for all the current children by385 // passing null.386 workInProgress.child = reconcileChildFibers(387 workInProgress,388 current.child,389 null,390 renderLanes,391 );392 // In the second pass, we mount the new children. The trick here is that we393 // pass null in place of where we usually pass the current child set. This has394 // the effect of remounting all children regardless of whether their395 // identities match.396 workInProgress.child = reconcileChildFibers(397 workInProgress,398 null,399 nextChildren,400 renderLanes,401 );402}403404function updateForwardRef(405 current: Fiber | null,406 workInProgress: Fiber,407 Component: any,408 nextProps: any,409 renderLanes: Lanes,410) {411 // TODO: current can be non-null here even if the component412 // hasn't yet mounted. This happens after the first render suspends.413 // We'll need to figure out if this is fine or can cause issues.414 let render = Component.render;415 if (__DEV__) {416 const resolvedRender = resolveTypeForHotReloading(render);417 if (resolvedRender !== render) {418 render = resolvedRender;419 if (current !== null) {420 didReceiveUpdate = true;421 }422 }423 }424 const ref = workInProgress.ref;425426 let propsWithoutRef;427 if ('ref' in nextProps) {428 // `ref` is just a prop now, but `forwardRef` expects it to not appear in429 // the props object. This used to happen in the JSX runtime, but now we do430 // it here.431 propsWithoutRef = {} as {[string]: any};432 for (const key in nextProps) {433 // Since `ref` should only appear in props via the JSX transform, we can434 // assume that this is a plain object. So we don't need a435 // hasOwnProperty check.436 if (key !== 'ref') {437 propsWithoutRef[key] = nextProps[key];438 }439 }440 } else {441 propsWithoutRef = nextProps;442 }443444 // The rest is a fork of updateFunctionComponent445 prepareToReadContext(workInProgress, renderLanes);446 if (enableSchedulingProfiler) {447 markComponentRenderStarted(workInProgress);448 }449450 const nextChildren = renderWithHooks(451 current,452 workInProgress,453 render,454 propsWithoutRef,455 ref,456 renderLanes,457 );458 const hasId = checkDidRenderIdHook();459460 if (enableSchedulingProfiler) {461 markComponentRenderStopped();462 }463464 if (current !== null && !didReceiveUpdate) {465 bailoutHooks(current, workInProgress, renderLanes);466 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);467 }468469 if (getIsHydrating() && hasId) {470 pushMaterializedTreeId(workInProgress);471 }472473 // React DevTools reads this flag.474 workInProgress.flags |= PerformedWork;475 reconcileChildren(current, workInProgress, nextChildren, renderLanes);476 return workInProgress.child;477}478479function updateMemoComponent(480 current: Fiber | null,481 workInProgress: Fiber,482 Component: any,483 nextProps: any,484 renderLanes: Lanes,485): null | Fiber {486 if (current === null) {487 const type = Component.type;488 if (isSimpleFunctionComponent(type) && Component.compare === null) {489 let resolvedType = type;490 if (__DEV__) {491 resolvedType = resolveTypeForHotReloading(type);492 }493 // If this is a plain function component without default props,494 // and with only the default shallow comparison, we upgrade it495 // to a SimpleMemoComponent to allow fast path updates.496 workInProgress.tag = SimpleMemoComponent;497 workInProgress.type = resolvedType;498 if (__DEV__) {499 validateFunctionComponentInDev(workInProgress, type);500 }501 return updateSimpleMemoComponent(502 current,503 workInProgress,504 resolvedType,505 nextProps,506 renderLanes,507 );508 }509 const child = createFiberFromTypeAndProps(510 Component.type,511 null,512 nextProps,513 workInProgress,514 workInProgress.mode,515 renderLanes,516 );517 child.ref = workInProgress.ref;518 child.return = workInProgress;519 workInProgress.child = child;520 return child;521 }522 const currentChild = current.child as any as Fiber; // This is always exactly one child523 const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(524 current,525 renderLanes,526 );527 if (!hasScheduledUpdateOrContext) {528 // This will be the props with resolved defaultProps,529 // unlike current.memoizedProps which will be the unresolved ones.530 const prevProps = currentChild.memoizedProps;531 // Default to shallow comparison532 let compare = Component.compare;533 compare = compare !== null ? compare : shallowEqual;534 if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {535 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);536 }537 }538 // React DevTools reads this flag.539 workInProgress.flags |= PerformedWork;540 const newChild = createWorkInProgress(currentChild, nextProps);541 newChild.ref = workInProgress.ref;542 newChild.return = workInProgress;543 workInProgress.child = newChild;544 return newChild;545}546547function updateSimpleMemoComponent(548 current: Fiber | null,549 workInProgress: Fiber,550 Component: any,551 nextProps: any,552 renderLanes: Lanes,553): null | Fiber {554 // TODO: current can be non-null here even if the component555 // hasn't yet mounted. This happens when the inner render suspends.556 // We'll need to figure out if this is fine or can cause issues.557 if (current !== null) {558 const prevProps = current.memoizedProps;559 if (560 shallowEqual(prevProps, nextProps) &&561 current.ref === workInProgress.ref &&562 // Prevent bailout if the implementation changed due to hot reload.563 (__DEV__ ? workInProgress.type === current.type : true)564 ) {565 didReceiveUpdate = false;566567 // The props are shallowly equal. Reuse the previous props object, like we568 // would during a normal fiber bailout.569 //570 // We don't have strong guarantees that the props object is referentially571 // equal during updates where we can't bail out anyway — like if the props572 // are shallowly equal, but there's a local state or context update in the573 // same batch.574 //575 // However, as a principle, we should aim to make the behavior consistent576 // across different ways of memoizing a component. For example, React.memo577 // has a different internal Fiber layout if you pass a normal function578 // component (SimpleMemoComponent) versus if you pass a different type579 // like forwardRef (MemoComponent). But this is an implementation detail.580 // Wrapping a component in forwardRef (or React.lazy, etc) shouldn't581 // affect whether the props object is reused during a bailout.582 workInProgress.pendingProps = nextProps = prevProps;583584 if (!checkScheduledUpdateOrContext(current, renderLanes)) {585 // The pending lanes were cleared at the beginning of beginWork. We're586 // about to bail out, but there might be other lanes that weren't587 // included in the current render. Usually, the priority level of the588 // remaining updates is accumulated during the evaluation of the589 // component (i.e. when processing the update queue). But since since590 // we're bailing out early *without* evaluating the component, we need591 // to account for it here, too. Reset to the value of the current fiber.592 // NOTE: This only applies to SimpleMemoComponent, not MemoComponent,593 // because a MemoComponent fiber does not have hooks or an update queue;594 // rather, it wraps around an inner component, which may or may not595 // contains hooks.596 // TODO: Move the reset at in beginWork out of the common path so that597 // this is no longer necessary.598 workInProgress.lanes = current.lanes;599 return bailoutOnAlreadyFinishedWork(600 current,601 workInProgress,602 renderLanes,603 );604 } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {605 // This is a special case that only exists for legacy mode.606 // See https://github.com/facebook/react/pull/19216.607 didReceiveUpdate = true;608 }609 }610 }611 return updateFunctionComponent(612 current,613 workInProgress,614 Component,615 nextProps,616 renderLanes,617 );618}619620function updateOffscreenComponent(621 current: Fiber | null,622 workInProgress: Fiber,623 renderLanes: Lanes,624 nextProps: OffscreenProps,625) {626 const nextChildren = nextProps.children;627628 const prevState: OffscreenState | null =629 current !== null ? current.memoizedState : null;630631 if (current === null && workInProgress.stateNode === null) {632 // We previously reset the work-in-progress.633 // We need to create a new Offscreen instance.634 const primaryChildInstance: OffscreenInstance = {635 _visibility: OffscreenVisible,636 _pendingMarkers: null,637 _retryCache: null,638 _transitions: null,639 };640 workInProgress.stateNode = primaryChildInstance;641 }642643 if (644 nextProps.mode === 'hidden' ||645 (enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')646 ) {647 // Rendering a hidden tree.648649 const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;650 if (didSuspend) {651 // Something suspended inside a hidden tree652653 // Include the base lanes from the last render654 const nextBaseLanes =655 prevState !== null656 ? mergeLanes(prevState.baseLanes, renderLanes)657 : renderLanes;658659 let remainingChildLanes;660 if (current !== null) {661 // Reset to the current children662 let currentChild = (workInProgress.child = current.child);663664 // The current render suspended, but there may be other lanes with665 // pending work. We can't read `childLanes` from the current Offscreen666 // fiber because we reset it when it was deferred; however, we can read667 // the pending lanes from the child fibers.668 let currentChildLanes: Lanes = NoLanes;669 while (currentChild !== null) {670 currentChildLanes = mergeLanes(671 mergeLanes(currentChildLanes, currentChild.lanes),672 currentChild.childLanes,673 );674 currentChild = currentChild.sibling;675 }676 const lanesWeJustAttempted = nextBaseLanes;677 remainingChildLanes = removeLanes(678 currentChildLanes,679 lanesWeJustAttempted,680 );681 } else {682 remainingChildLanes = NoLanes;683 workInProgress.child = null;684 }685686 return deferHiddenOffscreenComponent(687 current,688 workInProgress,689 nextBaseLanes,690 renderLanes,691 remainingChildLanes,692 );693 }694695 if (696 !disableLegacyMode &&697 (workInProgress.mode & ConcurrentMode) === NoMode698 ) {699 // In legacy sync mode, don't defer the subtree. Render it now.700 // TODO: Consider how Offscreen should work with transitions in the future701 const nextState: OffscreenState = {702 baseLanes: NoLanes,703 cachePool: null,704 };705 workInProgress.memoizedState = nextState;706 // push the cache pool even though we're going to bail out707 // because otherwise there'd be a context mismatch708 if (current !== null) {709 pushTransition(workInProgress, null, null);710 }711 reuseHiddenContextOnStack(workInProgress);712 pushOffscreenSuspenseHandler(workInProgress);713 } else if (!includesSomeLane(renderLanes, OffscreenLane as Lane)) {714 // We're hidden, and we're not rendering at Offscreen. We will bail out715 // and resume this tree later.716717 // Schedule this fiber to re-render at Offscreen priority718719 const remainingChildLanes = (workInProgress.lanes =720 laneToLanes(OffscreenLane));721722 // Include the base lanes from the last render723 const nextBaseLanes =724 prevState !== null725 ? mergeLanes(prevState.baseLanes, renderLanes)726 : renderLanes;727728 return deferHiddenOffscreenComponent(729 current,730 workInProgress,731 nextBaseLanes,732 renderLanes,733 remainingChildLanes,734 );735 } else {736 // This is the second render. The surrounding visible content has already737 // committed. Now we resume rendering the hidden tree.738739 // Rendering at offscreen, so we can clear the base lanes.740 const nextState: OffscreenState = {741 baseLanes: NoLanes,742 cachePool: null,743 };744 workInProgress.memoizedState = nextState;745 if (current !== null) {746 // If the render that spawned this one accessed the cache pool, resume747 // using the same cache. Unless the parent changed, since that means748 // there was a refresh.749 const prevCachePool = prevState !== null ? prevState.cachePool : null;750 // TODO: Consider if and how Offscreen pre-rendering should751 // be attributed to the transition that spawned it752 pushTransition(workInProgress, prevCachePool, null);753 }754755 // Push the lanes that were skipped when we bailed out.756 if (prevState !== null) {757 pushHiddenContext(workInProgress, prevState);758 } else {759 reuseHiddenContextOnStack(workInProgress);760 }761 pushOffscreenSuspenseHandler(workInProgress);762 }763 } else {764 // Rendering a visible tree.765 if (prevState !== null) {766 // We're going from hidden -> visible.767 let prevCachePool = null;768 // If the render that spawned this one accessed the cache pool, resume769 // using the same cache. Unless the parent changed, since that means770 // there was a refresh.771 prevCachePool = prevState.cachePool;772773 let transitions = null;774 if (enableTransitionTracing) {775 // We have now gone from hidden to visible, so any transitions should776 // be added to the stack to get added to any Offscreen/suspense children777 const instance: OffscreenInstance | null = workInProgress.stateNode;778 if (instance !== null && instance._transitions != null) {779 transitions = Array.from(instance._transitions);780 }781 }782783 pushTransition(workInProgress, prevCachePool, transitions);784785 // Push the lanes that were skipped when we bailed out.786 pushHiddenContext(workInProgress, prevState);787 reuseSuspenseHandlerOnStack(workInProgress);788789 // Since we're not hidden anymore, reset the state790 workInProgress.memoizedState = null;791 } else {792 // We weren't previously hidden, and we still aren't, so there's nothing793 // special to do. Need to push to the stack regardless, though, to avoid794 // a push/pop misalignment.795796 // If the render that spawned this one accessed the cache pool, resume797 // using the same cache. Unless the parent changed, since that means798 // there was a refresh.799 if (current !== null) {800 pushTransition(workInProgress, null, null);801 }802803 // We're about to bail out, but we need to push this to the stack anyway804 // to avoid a push/pop misalignment.805 reuseHiddenContextOnStack(workInProgress);806 reuseSuspenseHandlerOnStack(workInProgress);807 }808 }809810 reconcileChildren(current, workInProgress, nextChildren, renderLanes);811 return workInProgress.child;812}813814function bailoutOffscreenComponent(815 current: Fiber | null,816 workInProgress: Fiber,817): Fiber | null {818 if (819 (current === null || current.tag !== OffscreenComponent) &&820 workInProgress.stateNode === null821 ) {822 const primaryChildInstance: OffscreenInstance = {823 _visibility: OffscreenVisible,824 _pendingMarkers: null,825 _retryCache: null,826 _transitions: null,827 };828 workInProgress.stateNode = primaryChildInstance;829 }830831 return workInProgress.sibling;832}833834function deferHiddenOffscreenComponent(835 current: Fiber | null,836 workInProgress: Fiber,837 nextBaseLanes: Lanes,838 renderLanes: Lanes,839 remainingChildLanes: Lanes,840) {841 const nextState: OffscreenState = {842 baseLanes: nextBaseLanes,843 // Save the cache pool so we can resume later.844 cachePool: getOffscreenDeferredCache(),845 };846 workInProgress.memoizedState = nextState;847 // push the cache pool even though we're going to bail out848 // because otherwise there'd be a context mismatch849 if (current !== null) {850 pushTransition(workInProgress, null, null);851 }852853 // We're about to bail out, but we need to push this to the stack anyway854 // to avoid a push/pop misalignment.855 reuseHiddenContextOnStack(workInProgress);856857 pushOffscreenSuspenseHandler(workInProgress);858859 if (current !== null) {860 // Since this tree will resume rendering in a separate render, we need861 // to propagate parent contexts now so we don't lose track of which862 // ones changed.863 propagateParentContextChangesToDeferredTree(864 current,865 workInProgress,866 renderLanes,867 );868 }869870 // We override the remaining child lanes to be the subset that we computed871 // on the outside. We need to do this after propagating the context872 // because propagateParentContextChangesToDeferredTree may schedule873 // work which bubbles all the way up to the root and updates our child lanes.874 // We want to dismiss that since we're not going to work on it yet.875 workInProgress.childLanes = remainingChildLanes;876877 return null;878}879880function updateLegacyHiddenComponent(881 current: null | Fiber,882 workInProgress: Fiber,883 renderLanes: Lanes,884) {885 const nextProps: LegacyHiddenProps = workInProgress.pendingProps;886 // Note: These happen to have identical begin phases, for now. We shouldn't hold887 // ourselves to this constraint, though. If the behavior diverges, we should888 // fork the function.889 // This just works today because it has the same Props.890 return updateOffscreenComponent(891 current,892 workInProgress,893 renderLanes,894 nextProps,895 );896}897898function mountActivityChildren(899 workInProgress: Fiber,900 nextProps: ActivityProps,901 renderLanes: Lanes,902) {903 if (__DEV__) {904 const hiddenProp = (nextProps as any).hidden;905 if (hiddenProp !== undefined) {906 console.error(907 '<Activity> doesn\'t accept a hidden prop. Use mode="hidden" instead.\n' +908 '- <Activity %s>\n' +909 '+ <Activity %s>',910 hiddenProp === true911 ? 'hidden'912 : hiddenProp === false913 ? 'hidden={false}'914 : 'hidden={...}',915 hiddenProp ? 'mode="hidden"' : 'mode="visible"',916 );917 }918 }919 const nextChildren = nextProps.children;920 const nextMode = nextProps.mode;921 const mode = workInProgress.mode;922 const offscreenChildProps: OffscreenProps = {923 mode: nextMode,924 children: nextChildren,925 };926 const primaryChildFragment = mountWorkInProgressOffscreenFiber(927 offscreenChildProps,928 mode,929 renderLanes,930 );931 primaryChildFragment.ref = workInProgress.ref;932 workInProgress.child = primaryChildFragment;933 primaryChildFragment.return = workInProgress;934 return primaryChildFragment;935}936937function retryActivityComponentWithoutHydrating(938 current: Fiber,939 workInProgress: Fiber,940 renderLanes: Lanes,941) {942 // Falling back to client rendering. Because this has performance943 // implications, it's considered a recoverable error, even though the user944 // likely won't observe anything wrong with the UI.945946 // This will add the old fiber to the deletion list947 reconcileChildFibers(workInProgress, current.child, null, renderLanes);948949 // We're now not suspended nor dehydrated.950 const nextProps: ActivityProps = workInProgress.pendingProps;951 const primaryChildFragment = mountActivityChildren(952 workInProgress,953 nextProps,954 renderLanes,955 );956 // Needs a placement effect because the parent (the Activity boundary) already957 // mounted but this is a new fiber.958 primaryChildFragment.flags |= Placement;959960 // If we're not going to hydrate we can't leave it dehydrated if something961 // suspends. In that case we want that to bubble to the nearest parent boundary962 // so we need to pop our own handler that we just pushed.963 popSuspenseHandler(workInProgress);964965 workInProgress.memoizedState = null;966967 return primaryChildFragment;968}969970function mountDehydratedActivityComponent(971 workInProgress: Fiber,972 activityInstance: ActivityInstance,973 renderLanes: Lanes,974): null | Fiber {975 // During the first pass, we'll bail out and not drill into the children.976 // Instead, we'll leave the content in place and try to hydrate it later.977 // We'll continue hydrating the rest at offscreen priority since we'll already978 // be showing the right content coming from the server, it is no rush.979 workInProgress.lanes = laneToLanes(OffscreenLane);980 return null;981}982983function updateDehydratedActivityComponent(984 current: Fiber,985 workInProgress: Fiber,986 didSuspend: boolean,987 nextProps: ActivityProps,988 activityInstance: ActivityInstance,989 activityState: ActivityState,990 renderLanes: Lanes,991): null | Fiber {992 // We'll handle suspending since if something suspends we can just leave993 // it dehydrated. We push early and then pop if we enter non-dehydrated attempts.994 pushDehydratedActivitySuspenseHandler(workInProgress);995 if (!didSuspend) {996 // This is the first render pass. Attempt to hydrate.997998 // We should never be hydrating at this point because it is the first pass,999 // but after we've already committed once.1000 warnIfHydrating();10011002 if (includesSomeLane(renderLanes, OffscreenLane as Lane)) {1003 // If we're rendering Offscreen and we're entering the activity then it's possible1004 // that the only reason we rendered was because this boundary left work. Provide1005 // it as a cause if another one doesn't already exist.1006 markRenderDerivedCause(workInProgress);1007 }10081009 if (1010 // TODO: Factoring is a little weird, since we check this right below, too.1011 !didReceiveUpdate1012 ) {1013 // We need to check if any children have context before we decide to bail1014 // out, so propagate the changes now.1015 lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);1016 }10171018 // We use lanes to indicate that a child might depend on context, so if1019 // any context has changed, we need to treat is as if the input might have changed.1020 const hasContextChanged = includesSomeLane(renderLanes, current.childLanes);1021 if (didReceiveUpdate || hasContextChanged) {1022 // This boundary has changed since the first render. This means that we are now unable to1023 // hydrate it. We might still be able to hydrate it using a higher priority lane.1024 if (isCurrentTreeHidden()) {1025 // This boundary is inside a hidden subtree, where all work is1026 // deferred until the tree is revealed. Selective hydration works by1027 // rendering the boundary at a higher priority before the update1028 // applies, so it can't make progress here; delaying the commit to1029 // wait for it would deadlock. Replacing hidden content isn't1030 // visible, so give up and client render.1031 return retryActivityComponentWithoutHydrating(1032 current,1033 workInProgress,1034 renderLanes,1035 );1036 }1037 const root = getWorkInProgressRoot();1038 if (root !== null) {1039 const attemptHydrationAtLane = getBumpedLaneForHydration(1040 root,1041 renderLanes,1042 );1043 if (1044 attemptHydrationAtLane !== NoLane &&1045 attemptHydrationAtLane !== activityState.retryLane1046 ) {1047 // Intentionally mutating since this render will get interrupted. This1048 // is one of the very rare times where we mutate the current tree1049 // during the render phase.1050 activityState.retryLane = attemptHydrationAtLane;1051 enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);1052 scheduleUpdateOnFiber(root, current, attemptHydrationAtLane);10531054 // Throw a special object that signals to the work loop that it should1055 // interrupt the current render.1056 //1057 // Because we're inside a React-only execution stack, we don't1058 // strictly need to throw here — we could instead modify some internal1059 // work loop state. But using an exception means we don't need to1060 // check for this case on every iteration of the work loop. So doing1061 // it this way moves the check out of the fast path.1062 throw SelectiveHydrationException;1063 } else {1064 // We have already tried to ping at a higher priority than we're rendering with1065 // so if we got here, we must have failed to hydrate at those levels. We must1066 // now give up. Instead, we're going to delete the whole subtree and instead inject1067 // a new real Activity boundary to take its place. This might suspend for a while1068 // and if it does we might still have an opportunity to hydrate before this pass1069 // commits.1070 }1071 }10721073 // If we did not selectively hydrate, we'll continue rendering without1074 // hydrating. Mark this tree as suspended to prevent it from committing1075 // outside a transition.1076 //1077 // This path should only happen if the hydration lane already suspended.1078 renderDidSuspendDelayIfPossible();1079 return retryActivityComponentWithoutHydrating(1080 current,1081 workInProgress,1082 renderLanes,1083 );1084 } else {1085 // This is the first attempt.10861087 reenterHydrationStateFromDehydratedActivityInstance(1088 workInProgress,1089 activityInstance,1090 activityState.treeContext,1091 );10921093 const primaryChildFragment = mountActivityChildren(1094 workInProgress,1095 nextProps,1096 renderLanes,1097 );1098 // Mark the children as hydrating. This is a fast path to know whether this1099 // tree is part of a hydrating tree. This is used to determine if a child1100 // node has fully mounted yet, and for scheduling event replaying.1101 // Conceptually this is similar to Placement in that a new subtree is1102 // inserted into the React tree here. It just happens to not need DOM1103 // mutations because it already exists.1104 // We should still treat it as a newly inserted Fiber to double invoke Strict Effects.1105 primaryChildFragment.flags |= Hydrating | PlacementDEV;1106 return primaryChildFragment;1107 }1108 } else {1109 // This is the second render pass. We already attempted to hydrated, but1110 // something either suspended or errored.11111112 if (workInProgress.flags & ForceClientRender) {1113 // Something errored during hydration. Try again without hydrating.1114 // The error should've already been logged in throwException.1115 workInProgress.flags &= ~ForceClientRender;1116 return retryActivityComponentWithoutHydrating(1117 current,1118 workInProgress,1119 renderLanes,1120 );1121 } else if (1122 (workInProgress.memoizedState as null | ActivityState) !== null1123 ) {1124 // Something suspended and we should still be in dehydrated mode.1125 // Leave the existing child in place.11261127 workInProgress.child = current.child;1128 // The dehydrated completion pass expects this flag to be there1129 // but the normal offscreen pass doesn't.1130 workInProgress.flags |= DidCapture;1131 return null;1132 } else {1133 // We called retryActivityComponentWithoutHydrating and tried client rendering1134 // but now we suspended again. We should never arrive here because we should1135 // not have pushed a suspense handler during that second pass and it should1136 // instead have suspended above.1137 throw new Error(1138 'Client rendering an Activity suspended it again. This is a bug in React.',1139 );1140 }1141 }1142}11431144function updateActivityComponent(1145 current: null | Fiber,1146 workInProgress: Fiber,1147 renderLanes: Lanes,1148) {1149 const nextProps: ActivityProps = workInProgress.pendingProps;11501151 // Check if the first pass suspended.1152 const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;1153 workInProgress.flags &= ~DidCapture;11541155 if (current === null) {1156 // Initial mount11571158 // Special path for hydration1159 // If we're currently hydrating, try to hydrate this boundary.1160 // Hidden Activity boundaries are not emitted on the server.1161 if (getIsHydrating()) {1162 if (nextProps.mode === 'hidden') {1163 // SSR doesn't render hidden Activity so it shouldn't hydrate,1164 // even at offscreen lane. Defer to a client rendered offscreen lane.1165 const primaryChildFragment = mountActivityChildren(1166 workInProgress,1167 nextProps,1168 renderLanes,1169 );1170 workInProgress.lanes = laneToLanes(OffscreenLane);1171 // This tree hasn't been mounted yet so there are no baseLanes to carry over.1172 const nextState: OffscreenState = {1173 baseLanes: NoLanes,1174 cachePool: null,1175 };1176 primaryChildFragment.memoizedState = nextState;11771178 return bailoutOffscreenComponent(null, primaryChildFragment);1179 } else {1180 // We must push the suspense handler context *before* attempting to1181 // hydrate, to avoid a mismatch in case it errors.1182 pushDehydratedActivitySuspenseHandler(workInProgress);1183 const dehydrated: ActivityInstance =1184 claimNextHydratableActivityInstance(workInProgress);1185 return mountDehydratedActivityComponent(1186 workInProgress,1187 dehydrated,1188 renderLanes,1189 );1190 }1191 }11921193 return mountActivityChildren(workInProgress, nextProps, renderLanes);1194 } else {1195 // This is an update.11961197 // Special path for hydration1198 const prevState: null | ActivityState = current.memoizedState;11991200 if (prevState !== null) {1201 const dehydrated = prevState.dehydrated;1202 return updateDehydratedActivityComponent(1203 current,1204 workInProgress,1205 didSuspend,1206 nextProps,1207 dehydrated,1208 prevState,1209 renderLanes,1210 );1211 }12121213 const currentChild: Fiber = current.child as any;12141215 const nextChildren = nextProps.children;1216 const nextMode = nextProps.mode;1217 const offscreenChildProps: OffscreenProps = {1218 mode: nextMode,1219 children: nextChildren,1220 };12211222 if (1223 includesSomeLane(renderLanes, OffscreenLane as Lane) &&1224 includesSomeLane(renderLanes, current.lanes)1225 ) {1226 // If we're rendering Offscreen and we're entering the activity then it's possible1227 // that the only reason we rendered was because this boundary left work. Provide1228 // it as a cause if another one doesn't already exist.1229 markRenderDerivedCause(workInProgress);1230 }12311232 const primaryChildFragment = updateWorkInProgressOffscreenFiber(1233 currentChild,1234 offscreenChildProps,1235 );12361237 primaryChildFragment.ref = workInProgress.ref;1238 workInProgress.child = primaryChildFragment;1239 primaryChildFragment.return = workInProgress;1240 return primaryChildFragment;1241 }1242}12431244function updateCacheComponent(1245 current: Fiber | null,1246 workInProgress: Fiber,1247 renderLanes: Lanes,1248) {1249 prepareToReadContext(workInProgress, renderLanes);1250 const parentCache = readContext(CacheContext);12511252 if (current === null) {1253 // Initial mount. Request a fresh cache from the pool.1254 const freshCache = requestCacheFromPool(renderLanes);1255 const initialState: CacheComponentState = {1256 parent: parentCache,1257 cache: freshCache,1258 };1259 workInProgress.memoizedState = initialState;1260 initializeUpdateQueue(workInProgress);1261 pushCacheProvider(workInProgress, freshCache);1262 } else {1263 // Check for updates1264 if (includesSomeLane(current.lanes, renderLanes)) {1265 cloneUpdateQueue(current, workInProgress);1266 processUpdateQueue(workInProgress, null, null, renderLanes);1267 suspendIfUpdateReadFromEntangledAsyncAction();1268 }1269 const prevState: CacheComponentState = current.memoizedState;1270 const nextState: CacheComponentState = workInProgress.memoizedState;12711272 // Compare the new parent cache to the previous to see detect there was1273 // a refresh.1274 if (prevState.parent !== parentCache) {1275 // Refresh in parent. Update the parent.1276 const derivedState: CacheComponentState = {1277 parent: parentCache,1278 cache: parentCache,1279 };12801281 // Copied from getDerivedStateFromProps implementation. Once the update1282 // queue is empty, persist the derived state onto the base state.1283 workInProgress.memoizedState = derivedState;1284 if (workInProgress.lanes === NoLanes) {1285 const updateQueue: UpdateQueue<any> = workInProgress.updateQueue as any;1286 workInProgress.memoizedState = updateQueue.baseState = derivedState;1287 }12881289 pushCacheProvider(workInProgress, parentCache);1290 // No need to propagate a context change because the refreshed parent1291 // already did.1292 } else {1293 // The parent didn't refresh. Now check if this cache did.1294 const nextCache = nextState.cache;1295 pushCacheProvider(workInProgress, nextCache);1296 if (nextCache !== prevState.cache) {1297 // This cache refreshed. Propagate a context change.1298 propagateContextChange(workInProgress, CacheContext, renderLanes);1299 }1300 }1301 }13021303 const nextProps: CacheProps = workInProgress.pendingProps;13041305 const nextChildren = nextProps.children;1306 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1307 return workInProgress.child;1308}13091310// This should only be called if the name changes1311function updateTracingMarkerComponent(1312 current: Fiber | null,1313 workInProgress: Fiber,1314 renderLanes: Lanes,1315) {1316 if (!enableTransitionTracing) {1317 return null;1318 }13191320 const nextProps: TracingMarkerProps = workInProgress.pendingProps;13211322 // TODO: (luna) Only update the tracing marker if it's newly rendered or it's name changed.1323 // A tracing marker is only associated with the transitions that rendered1324 // or updated it, so we can create a new set of transitions each time1325 if (current === null) {1326 const currentTransitions = getPendingTransitions();1327 if (currentTransitions !== null) {1328 const markerInstance: TracingMarkerInstance = {1329 tag: TransitionTracingMarker,1330 transitions: new Set(currentTransitions),1331 pendingBoundaries: null,1332 name: nextProps.name,1333 aborts: null,1334 };1335 workInProgress.stateNode = markerInstance;13361337 // We call the marker complete callback when all child suspense boundaries resolve.1338 // We do this in the commit phase on Offscreen. If the marker has no child suspense1339 // boundaries, we need to schedule a passive effect to make sure we call the marker1340 // complete callback.1341 workInProgress.flags |= Passive;1342 }1343 } else {1344 if (__DEV__) {1345 if (current.memoizedProps.name !== nextProps.name) {1346 console.error(1347 'Changing the name of a tracing marker after mount is not supported. ' +1348 'To remount the tracing marker, pass it a new key.',1349 );1350 }1351 }1352 }13531354 const instance: TracingMarkerInstance | null = workInProgress.stateNode;1355 if (instance !== null) {1356 pushMarkerInstance(workInProgress, instance);1357 }1358 const nextChildren = nextProps.children;1359 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1360 return workInProgress.child;1361}13621363function updateFragment(1364 current: Fiber | null,1365 workInProgress: Fiber,1366 renderLanes: Lanes,1367) {1368 const nextChildren = workInProgress.pendingProps;1369 if (enableFragmentRefs) {1370 markRef(current, workInProgress);1371 }1372 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1373 return workInProgress.child;1374}13751376function updateMode(1377 current: Fiber | null,1378 workInProgress: Fiber,1379 renderLanes: Lanes,1380) {1381 const nextChildren = workInProgress.pendingProps.children;1382 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1383 return workInProgress.child;1384}13851386function updateProfiler(1387 current: Fiber | null,1388 workInProgress: Fiber,1389 renderLanes: Lanes,1390) {1391 if (enableProfilerTimer) {1392 workInProgress.flags |= Update;13931394 if (enableProfilerCommitHooks) {1395 // Schedule a passive effect for this Profiler to call onPostCommit hooks.1396 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,1397 // because the effect is also where times bubble to parent Profilers.1398 workInProgress.flags |= Passive;1399 // Reset effect durations for the next eventual effect phase.1400 // These are reset during render to allow the DevTools commit hook a chance to read them,1401 const stateNode = workInProgress.stateNode;1402 stateNode.effectDuration = -0;1403 stateNode.passiveEffectDuration = -0;1404 }1405 }1406 const nextProps: ProfilerProps = workInProgress.pendingProps;1407 const nextChildren = nextProps.children;1408 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1409 return workInProgress.child;1410}14111412function markRef(current: Fiber | null, workInProgress: Fiber) {1413 // TODO: Check props.ref instead of fiber.ref when enableRefAsProp is on.1414 const ref = workInProgress.ref;1415 if (ref === null) {1416 if (current !== null && current.ref !== null) {1417 // Schedule a Ref effect1418 workInProgress.flags |= Ref | RefStatic;1419 }1420 } else {1421 if (typeof ref !== 'function' && typeof ref !== 'object') {1422 throw new Error(1423 'Expected ref to be a function, an object returned by React.createRef(), or undefined/null.',1424 );1425 }1426 if (current === null || current.ref !== ref) {1427 // Schedule a Ref effect1428 workInProgress.flags |= Ref | RefStatic;1429 }1430 }1431}14321433function mountIncompleteFunctionComponent(1434 _current: null | Fiber,1435 workInProgress: Fiber,1436 Component: any,1437 nextProps: any,1438 renderLanes: Lanes,1439) {1440 resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);14411442 workInProgress.tag = FunctionComponent;14431444 return updateFunctionComponent(1445 null,1446 workInProgress,1447 Component,1448 nextProps,1449 renderLanes,1450 );1451}14521453function updateFunctionComponent(1454 current: null | Fiber,1455 workInProgress: Fiber,1456 Component: any,1457 nextProps: any,1458 renderLanes: Lanes,1459) {1460 if (__DEV__) {1461 if (1462 Component.prototype &&1463 typeof Component.prototype.render === 'function'1464 ) {1465 const componentName = getComponentNameFromType(Component) || 'Unknown';14661467 if (!didWarnAboutBadClass[componentName]) {1468 console.error(1469 "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +1470 'This is likely to cause errors. Change %s to extend React.Component instead.',1471 componentName,1472 componentName,1473 );1474 didWarnAboutBadClass[componentName] = true;1475 }1476 }14771478 if (workInProgress.mode & StrictLegacyMode) {1479 ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);1480 }14811482 if (current === null) {1483 // Some validations were previously done in mountIndeterminateComponent however and are now run1484 // in updateFuntionComponent but only on mount1485 validateFunctionComponentInDev(workInProgress, workInProgress.type);14861487 if (Component.contextTypes) {1488 const componentName = getComponentNameFromType(Component) || 'Unknown';14891490 if (!didWarnAboutContextTypes[componentName]) {1491 didWarnAboutContextTypes[componentName] = true;1492 if (disableLegacyContext) {1493 console.error(1494 '%s uses the legacy contextTypes API which was removed in React 19. ' +1495 'Use React.createContext() with React.useContext() instead. ' +1496 '(https://react.dev/link/legacy-context)',1497 componentName,1498 );1499 } else {1500 console.error(1501 '%s uses the legacy contextTypes API which will be removed soon. ' +1502 'Use React.createContext() with React.useContext() instead. ' +1503 '(https://react.dev/link/legacy-context)',1504 componentName,1505 );1506 }1507 }1508 }1509 }1510 }15111512 let context;1513 if (!disableLegacyContext && !disableLegacyContextForFunctionComponents) {1514 const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);1515 context = getMaskedContext(workInProgress, unmaskedContext);1516 }15171518 let nextChildren;1519 let hasId;1520 prepareToReadContext(workInProgress, renderLanes);1521 if (enableSchedulingProfiler) {1522 markComponentRenderStarted(workInProgress);1523 }1524 if (__DEV__) {1525 nextChildren = renderWithHooks(1526 current,1527 workInProgress,1528 Component,1529 nextProps,1530 context,1531 renderLanes,1532 );1533 hasId = checkDidRenderIdHook();1534 } else {1535 nextChildren = renderWithHooks(1536 current,1537 workInProgress,1538 Component,1539 nextProps,1540 context,1541 renderLanes,1542 );1543 hasId = checkDidRenderIdHook();1544 }1545 if (enableSchedulingProfiler) {1546 markComponentRenderStopped();1547 }15481549 if (current !== null && !didReceiveUpdate) {1550 bailoutHooks(current, workInProgress, renderLanes);1551 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1552 }15531554 if (getIsHydrating() && hasId) {1555 pushMaterializedTreeId(workInProgress);1556 }15571558 // React DevTools reads this flag.1559 workInProgress.flags |= PerformedWork;1560 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1561 return workInProgress.child;1562}15631564export function replayFunctionComponent(1565 current: Fiber | null,1566 workInProgress: Fiber,1567 nextProps: any,1568 Component: any,1569 secondArg: any,1570 renderLanes: Lanes,1571): Fiber | null {1572 // This function is used to replay a component that previously suspended,1573 // after its data resolves. It's a simplified version of1574 // updateFunctionComponent that reuses the hooks from the previous attempt.15751576 prepareToReadContext(workInProgress, renderLanes);1577 if (enableSchedulingProfiler) {1578 markComponentRenderStarted(workInProgress);1579 }1580 const nextChildren = replaySuspendedComponentWithHooks(1581 current,1582 workInProgress,1583 Component,1584 nextProps,1585 secondArg,1586 );1587 const hasId = checkDidRenderIdHook();1588 if (enableSchedulingProfiler) {1589 markComponentRenderStopped();1590 }15911592 if (current !== null && !didReceiveUpdate) {1593 bailoutHooks(current, workInProgress, renderLanes);1594 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1595 }15961597 if (getIsHydrating() && hasId) {1598 pushMaterializedTreeId(workInProgress);1599 }16001601 // React DevTools reads this flag.1602 workInProgress.flags |= PerformedWork;1603 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1604 return workInProgress.child;1605}16061607function updateClassComponent(1608 current: Fiber | null,1609 workInProgress: Fiber,1610 Component: any,1611 nextProps: any,1612 renderLanes: Lanes,1613) {1614 if (__DEV__) {1615 // This is used by DevTools to force a boundary to error.1616 switch (shouldError(workInProgress)) {1617 case false: {1618 // We previously simulated an error on this boundary1619 // so the instance must have been constructed in a previous1620 // commit.1621 const instance = workInProgress.stateNode;1622 const ctor = workInProgress.type;1623 // TODO This way of resetting the error boundary state is a hack.1624 // Is there a better way to do this?1625 const tempInstance = new ctor(1626 workInProgress.memoizedProps,1627 instance.context,1628 );1629 const state = tempInstance.state;1630 instance.updater.enqueueSetState(instance, state, null);1631 break;1632 }1633 case true: {1634 workInProgress.flags |= DidCapture;1635 workInProgress.flags |= ShouldCapture;1636 // eslint-disable-next-line react-internal/prod-error-codes1637 const error = new Error('Simulated error coming from DevTools');1638 const lane = pickArbitraryLane(renderLanes);1639 workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);1640 // Schedule the error boundary to re-render using updated state1641 const root: FiberRoot | null = getWorkInProgressRoot();1642 if (root === null) {1643 throw new Error(1644 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',1645 );1646 }1647 const update = createClassErrorUpdate(lane);1648 initializeClassErrorUpdate(1649 update,1650 root,1651 workInProgress,1652 createCapturedValueAtFiber(error, workInProgress),1653 );1654 enqueueCapturedUpdate(workInProgress, update);1655 break;1656 }1657 }1658 }16591660 // Push context providers early to prevent context stack mismatches.1661 // During mounting we don't know the child context yet as the instance doesn't exist.1662 // We will invalidate the child context in finishClassComponent() right after rendering.1663 let hasContext;1664 if (isLegacyContextProvider(Component)) {1665 hasContext = true;1666 pushLegacyContextProvider(workInProgress);1667 } else {1668 hasContext = false;1669 }1670 prepareToReadContext(workInProgress, renderLanes);16711672 const instance = workInProgress.stateNode;1673 let shouldUpdate;1674 if (instance === null) {1675 resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress);16761677 // In the initial pass we might need to construct the instance.1678 constructClassInstance(workInProgress, Component, nextProps);1679 mountClassInstance(workInProgress, Component, nextProps, renderLanes);1680 shouldUpdate = true;1681 } else if (current === null) {1682 // In a resume, we'll already have an instance we can reuse.1683 shouldUpdate = resumeMountClassInstance(1684 workInProgress,1685 Component,1686 nextProps,1687 renderLanes,1688 );1689 } else {1690 shouldUpdate = updateClassInstance(1691 current,1692 workInProgress,1693 Component,1694 nextProps,1695 renderLanes,1696 );1697 }1698 const nextUnitOfWork = finishClassComponent(1699 current,1700 workInProgress,1701 Component,1702 shouldUpdate,1703 hasContext,1704 renderLanes,1705 );1706 if (__DEV__) {1707 const inst = workInProgress.stateNode;1708 if (shouldUpdate && inst.props !== nextProps) {1709 if (!didWarnAboutReassigningProps) {1710 console.error(1711 'It looks like %s is reassigning its own `this.props` while rendering. ' +1712 'This is not supported and can lead to confusing bugs.',1713 getComponentNameFromFiber(workInProgress) || 'a component',1714 );1715 }1716 didWarnAboutReassigningProps = true;1717 }1718 }1719 return nextUnitOfWork;1720}17211722function finishClassComponent(1723 current: Fiber | null,1724 workInProgress: Fiber,1725 Component: any,1726 shouldUpdate: boolean,1727 hasContext: boolean,1728 renderLanes: Lanes,1729) {1730 // Refs should update even if shouldComponentUpdate returns false1731 markRef(current, workInProgress);17321733 const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;17341735 if (!shouldUpdate && !didCaptureError) {1736 // Context providers should defer to sCU for rendering1737 if (hasContext) {1738 invalidateContextProvider(workInProgress, Component, false);1739 }17401741 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1742 }17431744 const instance = workInProgress.stateNode;17451746 // Rerender1747 if (__DEV__) {1748 setCurrentFiber(workInProgress);1749 }1750 let nextChildren;1751 if (1752 didCaptureError &&1753 typeof Component.getDerivedStateFromError !== 'function'1754 ) {1755 // If we captured an error, but getDerivedStateFromError is not defined,1756 // unmount all the children. componentDidCatch will schedule an update to1757 // re-render a fallback. This is temporary until we migrate everyone to1758 // the new API.1759 // TODO: Warn in a future release.1760 nextChildren = null;17611762 if (enableProfilerTimer) {1763 stopProfilerTimerIfRunning(workInProgress);1764 }1765 } else {1766 if (enableSchedulingProfiler) {1767 markComponentRenderStarted(workInProgress);1768 }1769 if (__DEV__) {1770 nextChildren = callRenderInDEV(instance);1771 if (workInProgress.mode & StrictLegacyMode) {1772 setIsStrictModeForDevtools(true);1773 try {1774 callRenderInDEV(instance);1775 } finally {1776 setIsStrictModeForDevtools(false);1777 }1778 }1779 } else {1780 nextChildren = instance.render();1781 }1782 if (enableSchedulingProfiler) {1783 markComponentRenderStopped();1784 }1785 }17861787 // React DevTools reads this flag.1788 workInProgress.flags |= PerformedWork;1789 if (current !== null && didCaptureError) {1790 // If we're recovering from an error, reconcile without reusing any of1791 // the existing children. Conceptually, the normal children and the children1792 // that are shown on error are two different sets, so we shouldn't reuse1793 // normal children even if their identities match.1794 forceUnmountCurrentAndReconcile(1795 current,1796 workInProgress,1797 nextChildren,1798 renderLanes,1799 );1800 } else {1801 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1802 }18031804 // Memoize state using the values we just used to render.1805 // TODO: Restructure so we never read values from the instance.1806 workInProgress.memoizedState = instance.state;18071808 // The context might have changed so we need to recalculate it.1809 if (hasContext) {1810 invalidateContextProvider(workInProgress, Component, true);1811 }18121813 return workInProgress.child;1814}18151816function pushHostRootContext(workInProgress: Fiber) {1817 const root = workInProgress.stateNode as FiberRoot;1818 if (root.pendingContext) {1819 pushTopLevelContextObject(1820 workInProgress,1821 root.pendingContext,1822 root.pendingContext !== root.context,1823 );1824 } else if (root.context) {1825 // Should always be set1826 pushTopLevelContextObject(workInProgress, root.context, false);1827 }1828 pushHostContainer(workInProgress, root.containerInfo);1829}18301831function updateHostRoot(1832 current: null | Fiber,1833 workInProgress: Fiber,1834 renderLanes: Lanes,1835) {1836 pushHostRootContext(workInProgress);18371838 if (current === null) {1839 throw new Error('Should have a current fiber. This is a bug in React.');1840 }18411842 const nextProps = workInProgress.pendingProps;1843 const prevState: RootState = workInProgress.memoizedState;1844 const prevChildren = prevState.element;1845 cloneUpdateQueue(current, workInProgress);1846 processUpdateQueue(workInProgress, nextProps, null, renderLanes);18471848 const nextState: RootState = workInProgress.memoizedState;1849 const root: FiberRoot = workInProgress.stateNode;1850 pushRootTransition(workInProgress, root, renderLanes);18511852 if (enableTransitionTracing) {1853 pushRootMarkerInstance(workInProgress);1854 }18551856 const nextCache: Cache = nextState.cache;1857 pushCacheProvider(workInProgress, nextCache);1858 if (nextCache !== prevState.cache) {1859 // The root cache refreshed.1860 propagateContextChange(workInProgress, CacheContext, renderLanes);1861 }18621863 // This would ideally go inside processUpdateQueue, but because it suspends,1864 // it needs to happen after the `pushCacheProvider` call above to avoid a1865 // context stack mismatch. A bit unfortunate.1866 suspendIfUpdateReadFromEntangledAsyncAction();18671868 // Caution: React DevTools currently depends on this property1869 // being called "element".1870 const nextChildren = nextState.element;1871 // $FlowFixMe[constant-condition]1872 if (supportsHydration && prevState.isDehydrated) {1873 // This is a hydration root whose shell has not yet hydrated. We should1874 // attempt to hydrate.18751876 // Flip isDehydrated to false to indicate that when this render1877 // finishes, the root will no longer be dehydrated.1878 const overrideState: RootState = {1879 element: nextChildren,1880 isDehydrated: false,1881 cache: nextState.cache,1882 };1883 const updateQueue: UpdateQueue<RootState> =1884 workInProgress.updateQueue as any;1885 // `baseState` can always be the last state because the root doesn't1886 // have reducer functions so it doesn't need rebasing.1887 updateQueue.baseState = overrideState;1888 workInProgress.memoizedState = overrideState;18891890 if (workInProgress.flags & ForceClientRender) {1891 // Something errored during a previous attempt to hydrate the shell, so we1892 // forced a client render. We should have a recoverable error already scheduled.1893 return mountHostRootWithoutHydrating(1894 current,1895 workInProgress,1896 nextChildren,1897 renderLanes,1898 );1899 } else if (nextChildren !== prevChildren) {1900 const recoverableError = createCapturedValueAtFiber<mixed>(1901 new Error(1902 'This root received an early update, before anything was able ' +1903 'hydrate. Switched the entire root to client rendering.',1904 ),1905 workInProgress,1906 );1907 queueHydrationError(recoverableError);1908 return mountHostRootWithoutHydrating(1909 current,1910 workInProgress,1911 nextChildren,1912 renderLanes,1913 );1914 } else {1915 // The outermost shell has not hydrated yet. Start hydrating.1916 enterHydrationState(workInProgress);19171918 const child = mountChildFibers(1919 workInProgress,1920 null,1921 nextChildren,1922 renderLanes,1923 );1924 workInProgress.child = child;19251926 let node = child;1927 while (node) {1928 // Mark each child as hydrating. This is a fast path to know whether this1929 // tree is part of a hydrating tree. This is used to determine if a child1930 // node has fully mounted yet, and for scheduling event replaying.1931 // Conceptually this is similar to Placement in that a new subtree is1932 // inserted into the React tree here. It just happens to not need DOM1933 // mutations because it already exists.1934 // We should still treat it as a newly inserted Fiber to double invoke Strict Effects.1935 node.flags = (node.flags & ~Placement) | Hydrating | PlacementDEV;1936 node = node.sibling;1937 }1938 }1939 } else {1940 // Root is not dehydrated. Either this is a client-only root, or it1941 // already hydrated.1942 resetHydrationState();1943 if (nextChildren === prevChildren) {1944 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);1945 }1946 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1947 }1948 return workInProgress.child;1949}19501951function mountHostRootWithoutHydrating(1952 current: Fiber,1953 workInProgress: Fiber,1954 nextChildren: ReactNodeList,1955 renderLanes: Lanes,1956) {1957 // Revert to client rendering.1958 resetHydrationState();19591960 workInProgress.flags |= ForceClientRender;19611962 reconcileChildren(current, workInProgress, nextChildren, renderLanes);1963 return workInProgress.child;1964}19651966function updateHostComponent(1967 current: Fiber | null,1968 workInProgress: Fiber,1969 renderLanes: Lanes,1970) {1971 if (current === null) {1972 tryToClaimNextHydratableInstance(workInProgress);1973 }19741975 pushHostContext(workInProgress);19761977 const type = workInProgress.type;1978 const nextProps = workInProgress.pendingProps;1979 const prevProps = current !== null ? current.memoizedProps : null;19801981 let nextChildren = nextProps.children;1982 const isDirectTextChild = shouldSetTextContent(type, nextProps);19831984 if (isDirectTextChild) {1985 // We special case a direct text child of a host node. This is a common1986 // case. We won't handle it as a reified child. We will instead handle1987 // this in the host environment that also has access to this prop. That1988 // avoids allocating another HostText fiber and traversing it.1989 nextChildren = null;1990 } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {1991 // If we're switching from a direct text child to a normal child, or to1992 // empty, we need to schedule the text content to be reset.1993 workInProgress.flags |= ContentReset;1994 }19951996 const memoizedState = workInProgress.memoizedState;1997 if (memoizedState !== null) {1998 // This fiber has been upgraded to a stateful component. The only way1999 // happens currently is for form actions. We use hooks to track the2000 // pending and error state of the form.
Findings
✓ No findings reported for this file.