Best JavaScript code snippet using playwright-internal
ReactFiberCommitWork.js
Source:ReactFiberCommitWork.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */9import type {10 Instance,11 TextInstance,12 SuspenseInstance,13 Container,14 ChildSet,15 UpdatePayload,16} from './ReactFiberHostConfig';17import type {Fiber} from './ReactFiber';18import type {FiberRoot} from './ReactFiberRoot';19import type {ExpirationTime} from './ReactFiberExpirationTime';20import type {SuspenseState} from './ReactFiberSuspenseComponent';21import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';22import type {Wakeable} from 'shared/ReactTypes';23import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';24import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';25import {26 deferPassiveEffectCleanupDuringUnmount,27 enableSchedulerTracing,28 enableProfilerTimer,29 enableProfilerCommitHooks,30 enableSuspenseServerRenderer,31 enableDeprecatedFlareAPI,32 enableFundamentalAPI,33 enableSuspenseCallback,34 enableScopeAPI,35 runAllPassiveEffectDestroysBeforeCreates,36 enableUseEventAPI,37} from 'shared/ReactFeatureFlags';38import {39 FunctionComponent,40 ForwardRef,41 ClassComponent,42 HostRoot,43 HostComponent,44 HostText,45 HostPortal,46 Profiler,47 SuspenseComponent,48 DehydratedFragment,49 IncompleteClassComponent,50 MemoComponent,51 SimpleMemoComponent,52 SuspenseListComponent,53 FundamentalComponent,54 ScopeComponent,55 Block,56} from './ReactWorkTags';57import {58 invokeGuardedCallback,59 hasCaughtError,60 clearCaughtError,61} from 'shared/ReactErrorUtils';62import {63 NoEffect,64 ContentReset,65 Placement,66 Snapshot,67 Update,68 Passive,69} from './ReactSideEffectTags';70import getComponentName from 'shared/getComponentName';71import invariant from 'shared/invariant';72import {onCommitUnmount} from './ReactFiberDevToolsHook';73import {getStackByFiberInDevAndProd} from './ReactCurrentFiber';74import {resolveDefaultProps} from './ReactFiberLazyComponent';75import {76 getCommitTime,77 recordLayoutEffectDuration,78 recordPassiveEffectDuration,79 startLayoutEffectTimer,80 startPassiveEffectTimer,81} from './ReactProfilerTimer';82import {ProfileMode} from './ReactTypeOfMode';83import {commitUpdateQueue} from './ReactUpdateQueue';84import {85 getPublicInstance,86 supportsMutation,87 supportsPersistence,88 supportsHydration,89 commitMount,90 commitUpdate,91 resetTextContent,92 commitTextUpdate,93 appendChild,94 appendChildToContainer,95 insertBefore,96 insertInContainerBefore,97 removeChild,98 removeChildFromContainer,99 clearSuspenseBoundary,100 clearSuspenseBoundaryFromContainer,101 replaceContainerChildren,102 createContainerChildSet,103 hideInstance,104 hideTextInstance,105 unhideInstance,106 unhideTextInstance,107 unmountFundamentalComponent,108 updateFundamentalComponent,109 commitHydratedContainer,110 commitHydratedSuspenseInstance,111 beforeRemoveInstance,112} from './ReactFiberHostConfig';113import {114 captureCommitPhaseError,115 resolveRetryWakeable,116 markCommitTimeOfFallback,117 enqueuePendingPassiveHookEffectMount,118 enqueuePendingPassiveHookEffectUnmount,119 enqueuePendingPassiveProfilerEffect,120} from './ReactFiberWorkLoop';121import {122 NoEffect as NoHookEffect,123 HasEffect as HookHasEffect,124 Layout as HookLayout,125 Passive as HookPassive,126} from './ReactHookEffectTags';127import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';128import {runWithPriority, NormalPriority} from './SchedulerWithReactIntegration';129import {130 updateDeprecatedEventListeners,131 unmountDeprecatedResponderListeners,132} from './ReactFiberDeprecatedEvents';133let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;134if (__DEV__) {135 didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();136}137const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;138const callComponentWillUnmountWithTimer = function(current, instance) {139 instance.props = current.memoizedProps;140 instance.state = current.memoizedState;141 if (142 enableProfilerTimer &&143 enableProfilerCommitHooks &&144 current.mode & ProfileMode145 ) {146 try {147 startLayoutEffectTimer();148 instance.componentWillUnmount();149 } finally {150 recordLayoutEffectDuration(current);151 }152 } else {153 instance.componentWillUnmount();154 }155};156// Capture errors so they don't interrupt unmounting.157function safelyCallComponentWillUnmount(current, instance) {158 if (__DEV__) {159 invokeGuardedCallback(160 null,161 callComponentWillUnmountWithTimer,162 null,163 current,164 instance,165 );166 if (hasCaughtError()) {167 const unmountError = clearCaughtError();168 captureCommitPhaseError(current, unmountError);169 }170 } else {171 try {172 callComponentWillUnmountWithTimer(current, instance);173 } catch (unmountError) {174 captureCommitPhaseError(current, unmountError);175 }176 }177}178function safelyDetachRef(current: Fiber) {179 const ref = current.ref;180 if (ref !== null) {181 if (typeof ref === 'function') {182 if (__DEV__) {183 invokeGuardedCallback(null, ref, null, null);184 if (hasCaughtError()) {185 const refError = clearCaughtError();186 captureCommitPhaseError(current, refError);187 }188 } else {189 try {190 ref(null);191 } catch (refError) {192 captureCommitPhaseError(current, refError);193 }194 }195 } else {196 ref.current = null;197 }198 }199}200function safelyCallDestroy(current, destroy) {201 if (__DEV__) {202 invokeGuardedCallback(null, destroy, null);203 if (hasCaughtError()) {204 const error = clearCaughtError();205 captureCommitPhaseError(current, error);206 }207 } else {208 try {209 destroy();210 } catch (error) {211 captureCommitPhaseError(current, error);212 }213 }214}215function commitBeforeMutationLifeCycles(216 current: Fiber | null,217 finishedWork: Fiber,218): void {219 switch (finishedWork.tag) {220 case FunctionComponent:221 case ForwardRef:222 case SimpleMemoComponent:223 case Block: {224 return;225 }226 case ClassComponent: {227 if (finishedWork.effectTag & Snapshot) {228 if (current !== null) {229 const prevProps = current.memoizedProps;230 const prevState = current.memoizedState;231 const instance = finishedWork.stateNode;232 // We could update instance props and state here,233 // but instead we rely on them being set during last render.234 // TODO: revisit this when we implement resuming.235 if (__DEV__) {236 if (237 finishedWork.type === finishedWork.elementType &&238 !didWarnAboutReassigningProps239 ) {240 if (instance.props !== finishedWork.memoizedProps) {241 console.error(242 'Expected %s props to match memoized props before ' +243 'getSnapshotBeforeUpdate. ' +244 'This might either be because of a bug in React, or because ' +245 'a component reassigns its own `this.props`. ' +246 'Please file an issue.',247 getComponentName(finishedWork.type) || 'instance',248 );249 }250 if (instance.state !== finishedWork.memoizedState) {251 console.error(252 'Expected %s state to match memoized state before ' +253 'getSnapshotBeforeUpdate. ' +254 'This might either be because of a bug in React, or because ' +255 'a component reassigns its own `this.state`. ' +256 'Please file an issue.',257 getComponentName(finishedWork.type) || 'instance',258 );259 }260 }261 }262 const snapshot = instance.getSnapshotBeforeUpdate(263 finishedWork.elementType === finishedWork.type264 ? prevProps265 : resolveDefaultProps(finishedWork.type, prevProps),266 prevState,267 );268 if (__DEV__) {269 const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);270 if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {271 didWarnSet.add(finishedWork.type);272 console.error(273 '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +274 'must be returned. You have returned undefined.',275 getComponentName(finishedWork.type),276 );277 }278 }279 instance.__reactInternalSnapshotBeforeUpdate = snapshot;280 }281 }282 return;283 }284 case HostRoot:285 case HostComponent:286 case HostText:287 case HostPortal:288 case IncompleteClassComponent:289 // Nothing to do for these component types290 return;291 }292 invariant(293 false,294 'This unit of work tag should not have side-effects. This error is ' +295 'likely caused by a bug in React. Please file an issue.',296 );297}298function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {299 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);300 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;301 if (lastEffect !== null) {302 const firstEffect = lastEffect.next;303 let effect = firstEffect;304 do {305 if ((effect.tag & tag) === tag) {306 // Unmount307 const destroy = effect.destroy;308 effect.destroy = undefined;309 if (destroy !== undefined) {310 destroy();311 }312 }313 effect = effect.next;314 } while (effect !== firstEffect);315 }316}317function commitHookEffectListMount(tag: number, finishedWork: Fiber) {318 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);319 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;320 if (lastEffect !== null) {321 const firstEffect = lastEffect.next;322 let effect = firstEffect;323 do {324 if ((effect.tag & tag) === tag) {325 // Mount326 const create = effect.create;327 effect.destroy = create();328 if (__DEV__) {329 const destroy = effect.destroy;330 if (destroy !== undefined && typeof destroy !== 'function') {331 let addendum;332 if (destroy === null) {333 addendum =334 ' You returned null. If your effect does not require clean ' +335 'up, return undefined (or nothing).';336 } else if (typeof destroy.then === 'function') {337 addendum =338 '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +339 'Instead, write the async function inside your effect ' +340 'and call it immediately:\n\n' +341 'useEffect(() => {\n' +342 ' async function fetchData() {\n' +343 ' // You can await here\n' +344 ' const response = await MyAPI.getData(someId);\n' +345 ' // ...\n' +346 ' }\n' +347 ' fetchData();\n' +348 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +349 'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching';350 } else {351 addendum = ' You returned: ' + destroy;352 }353 console.error(354 'An effect function must not return anything besides a function, ' +355 'which is used for clean-up.%s%s',356 addendum,357 getStackByFiberInDevAndProd(finishedWork),358 );359 }360 }361 }362 effect = effect.next;363 } while (effect !== firstEffect);364 }365}366function schedulePassiveEffects(finishedWork: Fiber) {367 if (runAllPassiveEffectDestroysBeforeCreates) {368 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);369 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;370 if (lastEffect !== null) {371 const firstEffect = lastEffect.next;372 let effect = firstEffect;373 do {374 const {next, tag} = effect;375 if (376 (tag & HookPassive) !== NoHookEffect &&377 (tag & HookHasEffect) !== NoHookEffect378 ) {379 enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);380 enqueuePendingPassiveHookEffectMount(finishedWork, effect);381 }382 effect = next;383 } while (effect !== firstEffect);384 }385 }386}387export function commitPassiveHookEffects(finishedWork: Fiber): void {388 if ((finishedWork.effectTag & Passive) !== NoEffect) {389 switch (finishedWork.tag) {390 case FunctionComponent:391 case ForwardRef:392 case SimpleMemoComponent:393 case Block: {394 // TODO (#17945) We should call all passive destroy functions (for all fibers)395 // before calling any create functions. The current approach only serializes396 // these for a single fiber.397 if (398 enableProfilerTimer &&399 enableProfilerCommitHooks &&400 finishedWork.mode & ProfileMode401 ) {402 try {403 startPassiveEffectTimer();404 commitHookEffectListUnmount(405 HookPassive | HookHasEffect,406 finishedWork,407 );408 commitHookEffectListMount(409 HookPassive | HookHasEffect,410 finishedWork,411 );412 } finally {413 recordPassiveEffectDuration(finishedWork);414 }415 } else {416 commitHookEffectListUnmount(417 HookPassive | HookHasEffect,418 finishedWork,419 );420 commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);421 }422 break;423 }424 default:425 break;426 }427 }428}429export function commitPassiveEffectDurations(430 finishedRoot: FiberRoot,431 finishedWork: Fiber,432): void {433 if (enableProfilerTimer && enableProfilerCommitHooks) {434 // Only Profilers with work in their subtree will have an Update effect scheduled.435 if ((finishedWork.effectTag & Update) !== NoEffect) {436 switch (finishedWork.tag) {437 case Profiler: {438 const {passiveEffectDuration} = finishedWork.stateNode;439 const {id, onPostCommit} = finishedWork.memoizedProps;440 // This value will still reflect the previous commit phase.441 // It does not get reset until the start of the next commit phase.442 const commitTime = getCommitTime();443 if (typeof onPostCommit === 'function') {444 if (enableSchedulerTracing) {445 onPostCommit(446 id,447 finishedWork.alternate === null ? 'mount' : 'update',448 passiveEffectDuration,449 commitTime,450 finishedRoot.memoizedInteractions,451 );452 } else {453 onPostCommit(454 id,455 finishedWork.alternate === null ? 'mount' : 'update',456 passiveEffectDuration,457 commitTime,458 );459 }460 }461 // Bubble times to the next nearest ancestor Profiler.462 // After we process that Profiler, we'll bubble further up.463 let parentFiber = finishedWork.return;464 while (parentFiber !== null) {465 if (parentFiber.tag === Profiler) {466 const parentStateNode = parentFiber.stateNode;467 parentStateNode.passiveEffectDuration += passiveEffectDuration;468 break;469 }470 parentFiber = parentFiber.return;471 }472 break;473 }474 default:475 break;476 }477 }478 }479}480function commitLifeCycles(481 finishedRoot: FiberRoot,482 current: Fiber | null,483 finishedWork: Fiber,484 committedExpirationTime: ExpirationTime,485): void {486 switch (finishedWork.tag) {487 case FunctionComponent:488 case ForwardRef:489 case SimpleMemoComponent:490 case Block: {491 // At this point layout effects have already been destroyed (during mutation phase).492 // This is done to prevent sibling component effects from interfering with each other,493 // e.g. a destroy function in one component should never override a ref set494 // by a create function in another component during the same commit.495 if (496 enableProfilerTimer &&497 enableProfilerCommitHooks &&498 finishedWork.mode & ProfileMode499 ) {500 try {501 startLayoutEffectTimer();502 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);503 } finally {504 recordLayoutEffectDuration(finishedWork);505 }506 } else {507 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);508 }509 if (runAllPassiveEffectDestroysBeforeCreates) {510 schedulePassiveEffects(finishedWork);511 }512 return;513 }514 case ClassComponent: {515 const instance = finishedWork.stateNode;516 if (finishedWork.effectTag & Update) {517 if (current === null) {518 // We could update instance props and state here,519 // but instead we rely on them being set during last render.520 // TODO: revisit this when we implement resuming.521 if (__DEV__) {522 if (523 finishedWork.type === finishedWork.elementType &&524 !didWarnAboutReassigningProps525 ) {526 if (instance.props !== finishedWork.memoizedProps) {527 console.error(528 'Expected %s props to match memoized props before ' +529 'componentDidMount. ' +530 'This might either be because of a bug in React, or because ' +531 'a component reassigns its own `this.props`. ' +532 'Please file an issue.',533 getComponentName(finishedWork.type) || 'instance',534 );535 }536 if (instance.state !== finishedWork.memoizedState) {537 console.error(538 'Expected %s state to match memoized state before ' +539 'componentDidMount. ' +540 'This might either be because of a bug in React, or because ' +541 'a component reassigns its own `this.state`. ' +542 'Please file an issue.',543 getComponentName(finishedWork.type) || 'instance',544 );545 }546 }547 }548 if (549 enableProfilerTimer &&550 enableProfilerCommitHooks &&551 finishedWork.mode & ProfileMode552 ) {553 try {554 startLayoutEffectTimer();555 instance.componentDidMount();556 } finally {557 recordLayoutEffectDuration(finishedWork);558 }559 } else {560 instance.componentDidMount();561 }562 } else {563 const prevProps =564 finishedWork.elementType === finishedWork.type565 ? current.memoizedProps566 : resolveDefaultProps(finishedWork.type, current.memoizedProps);567 const prevState = current.memoizedState;568 // We could update instance props and state here,569 // but instead we rely on them being set during last render.570 // TODO: revisit this when we implement resuming.571 if (__DEV__) {572 if (573 finishedWork.type === finishedWork.elementType &&574 !didWarnAboutReassigningProps575 ) {576 if (instance.props !== finishedWork.memoizedProps) {577 console.error(578 'Expected %s props to match memoized props before ' +579 'componentDidUpdate. ' +580 'This might either be because of a bug in React, or because ' +581 'a component reassigns its own `this.props`. ' +582 'Please file an issue.',583 getComponentName(finishedWork.type) || 'instance',584 );585 }586 if (instance.state !== finishedWork.memoizedState) {587 console.error(588 'Expected %s state to match memoized state before ' +589 'componentDidUpdate. ' +590 'This might either be because of a bug in React, or because ' +591 'a component reassigns its own `this.state`. ' +592 'Please file an issue.',593 getComponentName(finishedWork.type) || 'instance',594 );595 }596 }597 }598 if (599 enableProfilerTimer &&600 enableProfilerCommitHooks &&601 finishedWork.mode & ProfileMode602 ) {603 try {604 startLayoutEffectTimer();605 instance.componentDidUpdate(606 prevProps,607 prevState,608 instance.__reactInternalSnapshotBeforeUpdate,609 );610 } finally {611 recordLayoutEffectDuration(finishedWork);612 }613 } else {614 instance.componentDidUpdate(615 prevProps,616 prevState,617 instance.__reactInternalSnapshotBeforeUpdate,618 );619 }620 }621 }622 const updateQueue = finishedWork.updateQueue;623 if (updateQueue !== null) {624 if (__DEV__) {625 if (626 finishedWork.type === finishedWork.elementType &&627 !didWarnAboutReassigningProps628 ) {629 if (instance.props !== finishedWork.memoizedProps) {630 console.error(631 'Expected %s props to match memoized props before ' +632 'processing the update queue. ' +633 'This might either be because of a bug in React, or because ' +634 'a component reassigns its own `this.props`. ' +635 'Please file an issue.',636 getComponentName(finishedWork.type) || 'instance',637 );638 }639 if (instance.state !== finishedWork.memoizedState) {640 console.error(641 'Expected %s state to match memoized state before ' +642 'processing the update queue. ' +643 'This might either be because of a bug in React, or because ' +644 'a component reassigns its own `this.state`. ' +645 'Please file an issue.',646 getComponentName(finishedWork.type) || 'instance',647 );648 }649 }650 }651 // We could update instance props and state here,652 // but instead we rely on them being set during last render.653 // TODO: revisit this when we implement resuming.654 commitUpdateQueue(finishedWork, updateQueue, instance);655 }656 return;657 }658 case HostRoot: {659 const updateQueue = finishedWork.updateQueue;660 if (updateQueue !== null) {661 let instance = null;662 if (finishedWork.child !== null) {663 switch (finishedWork.child.tag) {664 case HostComponent:665 instance = getPublicInstance(finishedWork.child.stateNode);666 break;667 case ClassComponent:668 instance = finishedWork.child.stateNode;669 break;670 }671 }672 commitUpdateQueue(finishedWork, updateQueue, instance);673 }674 return;675 }676 case HostComponent: {677 const instance: Instance = finishedWork.stateNode;678 // Renderers may schedule work to be done after host components are mounted679 // (eg DOM renderer may schedule auto-focus for inputs and form controls).680 // These effects should only be committed when components are first mounted,681 // aka when there is no current/alternate.682 if (current === null && finishedWork.effectTag & Update) {683 const type = finishedWork.type;684 const props = finishedWork.memoizedProps;685 commitMount(instance, type, props, finishedWork);686 }687 return;688 }689 case HostText: {690 // We have no life-cycles associated with text.691 return;692 }693 case HostPortal: {694 // We have no life-cycles associated with portals.695 return;696 }697 case Profiler: {698 if (enableProfilerTimer) {699 const {onCommit, onRender} = finishedWork.memoizedProps;700 const {effectDuration} = finishedWork.stateNode;701 const commitTime = getCommitTime();702 if (typeof onRender === 'function') {703 if (enableSchedulerTracing) {704 onRender(705 finishedWork.memoizedProps.id,706 current === null ? 'mount' : 'update',707 finishedWork.actualDuration,708 finishedWork.treeBaseDuration,709 finishedWork.actualStartTime,710 commitTime,711 finishedRoot.memoizedInteractions,712 );713 } else {714 onRender(715 finishedWork.memoizedProps.id,716 current === null ? 'mount' : 'update',717 finishedWork.actualDuration,718 finishedWork.treeBaseDuration,719 finishedWork.actualStartTime,720 commitTime,721 );722 }723 }724 if (enableProfilerCommitHooks) {725 if (typeof onCommit === 'function') {726 if (enableSchedulerTracing) {727 onCommit(728 finishedWork.memoizedProps.id,729 current === null ? 'mount' : 'update',730 effectDuration,731 commitTime,732 finishedRoot.memoizedInteractions,733 );734 } else {735 onCommit(736 finishedWork.memoizedProps.id,737 current === null ? 'mount' : 'update',738 effectDuration,739 commitTime,740 );741 }742 }743 // Schedule a passive effect for this Profiler to call onPostCommit hooks.744 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,745 // because the effect is also where times bubble to parent Profilers.746 enqueuePendingPassiveProfilerEffect(finishedWork);747 // Propagate layout effect durations to the next nearest Profiler ancestor.748 // Do not reset these values until the next render so DevTools has a chance to read them first.749 let parentFiber = finishedWork.return;750 while (parentFiber !== null) {751 if (parentFiber.tag === Profiler) {752 const parentStateNode = parentFiber.stateNode;753 parentStateNode.effectDuration += effectDuration;754 break;755 }756 parentFiber = parentFiber.return;757 }758 }759 }760 return;761 }762 case SuspenseComponent: {763 commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);764 return;765 }766 case SuspenseListComponent:767 case IncompleteClassComponent:768 case FundamentalComponent:769 case ScopeComponent:770 return;771 }772 invariant(773 false,774 'This unit of work tag should not have side-effects. This error is ' +775 'likely caused by a bug in React. Please file an issue.',776 );777}778function hideOrUnhideAllChildren(finishedWork, isHidden) {779 if (supportsMutation) {780 // We only have the top Fiber that was inserted but we need to recurse down its781 // children to find all the terminal nodes.782 let node: Fiber = finishedWork;783 while (true) {784 if (node.tag === HostComponent) {785 const instance = node.stateNode;786 if (isHidden) {787 hideInstance(instance);788 } else {789 unhideInstance(node.stateNode, node.memoizedProps);790 }791 } else if (node.tag === HostText) {792 const instance = node.stateNode;793 if (isHidden) {794 hideTextInstance(instance);795 } else {796 unhideTextInstance(instance, node.memoizedProps);797 }798 } else if (799 node.tag === SuspenseComponent &&800 node.memoizedState !== null &&801 node.memoizedState.dehydrated === null802 ) {803 // Found a nested Suspense component that timed out. Skip over the804 // primary child fragment, which should remain hidden.805 const fallbackChildFragment: Fiber = (node.child: any).sibling;806 fallbackChildFragment.return = node;807 node = fallbackChildFragment;808 continue;809 } else if (node.child !== null) {810 node.child.return = node;811 node = node.child;812 continue;813 }814 if (node === finishedWork) {815 return;816 }817 while (node.sibling === null) {818 if (node.return === null || node.return === finishedWork) {819 return;820 }821 node = node.return;822 }823 node.sibling.return = node.return;824 node = node.sibling;825 }826 }827}828function commitAttachRef(finishedWork: Fiber) {829 const ref = finishedWork.ref;830 if (ref !== null) {831 const instance = finishedWork.stateNode;832 let instanceToUse;833 switch (finishedWork.tag) {834 case HostComponent:835 instanceToUse = getPublicInstance(instance);836 break;837 default:838 instanceToUse = instance;839 }840 // Moved outside to ensure DCE works with this flag841 if (enableScopeAPI && finishedWork.tag === ScopeComponent) {842 instanceToUse = instance.methods;843 }844 if (typeof ref === 'function') {845 ref(instanceToUse);846 } else {847 if (__DEV__) {848 if (!ref.hasOwnProperty('current')) {849 console.error(850 'Unexpected ref object provided for %s. ' +851 'Use either a ref-setter function or React.createRef().%s',852 getComponentName(finishedWork.type),853 getStackByFiberInDevAndProd(finishedWork),854 );855 }856 }857 ref.current = instanceToUse;858 }859 }860}861function commitDetachRef(current: Fiber) {862 const currentRef = current.ref;863 if (currentRef !== null) {864 if (typeof currentRef === 'function') {865 currentRef(null);866 } else {867 currentRef.current = null;868 }869 }870}871// User-originating errors (lifecycles and refs) should not interrupt872// deletion, so don't let them throw. Host-originating errors should873// interrupt deletion, so it's okay874function commitUnmount(875 finishedRoot: FiberRoot,876 current: Fiber,877 renderPriorityLevel: ReactPriorityLevel,878): void {879 onCommitUnmount(current);880 switch (current.tag) {881 case FunctionComponent:882 case ForwardRef:883 case MemoComponent:884 case SimpleMemoComponent:885 case Block: {886 const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);887 if (updateQueue !== null) {888 const lastEffect = updateQueue.lastEffect;889 if (lastEffect !== null) {890 const firstEffect = lastEffect.next;891 if (892 deferPassiveEffectCleanupDuringUnmount &&893 runAllPassiveEffectDestroysBeforeCreates894 ) {895 let effect = firstEffect;896 do {897 const {destroy, tag} = effect;898 if (destroy !== undefined) {899 if ((tag & HookPassive) !== NoHookEffect) {900 enqueuePendingPassiveHookEffectUnmount(current, effect);901 } else {902 if (903 enableProfilerTimer &&904 enableProfilerCommitHooks &&905 current.mode & ProfileMode906 ) {907 startLayoutEffectTimer();908 safelyCallDestroy(current, destroy);909 recordLayoutEffectDuration(current);910 } else {911 safelyCallDestroy(current, destroy);912 }913 }914 }915 effect = effect.next;916 } while (effect !== firstEffect);917 } else {918 // When the owner fiber is deleted, the destroy function of a passive919 // effect hook is called during the synchronous commit phase. This is920 // a concession to implementation complexity. Calling it in the921 // passive effect phase (like they usually are, when dependencies922 // change during an update) would require either traversing the923 // children of the deleted fiber again, or including unmount effects924 // as part of the fiber effect list.925 //926 // Because this is during the sync commit phase, we need to change927 // the priority.928 //929 // TODO: Reconsider this implementation trade off.930 const priorityLevel =931 renderPriorityLevel > NormalPriority932 ? NormalPriority933 : renderPriorityLevel;934 runWithPriority(priorityLevel, () => {935 let effect = firstEffect;936 do {937 const {destroy, tag} = effect;938 if (destroy !== undefined) {939 if (940 enableProfilerTimer &&941 enableProfilerCommitHooks &&942 current.mode & ProfileMode943 ) {944 if ((tag & HookPassive) !== NoHookEffect) {945 safelyCallDestroy(current, destroy);946 } else {947 startLayoutEffectTimer();948 safelyCallDestroy(current, destroy);949 recordLayoutEffectDuration(current);950 }951 } else {952 safelyCallDestroy(current, destroy);953 }954 }955 effect = effect.next;956 } while (effect !== firstEffect);957 });958 }959 }960 }961 return;962 }963 case ClassComponent: {964 safelyDetachRef(current);965 const instance = current.stateNode;966 if (typeof instance.componentWillUnmount === 'function') {967 safelyCallComponentWillUnmount(current, instance);968 }969 return;970 }971 case HostComponent: {972 if (enableDeprecatedFlareAPI) {973 unmountDeprecatedResponderListeners(current);974 }975 if (enableDeprecatedFlareAPI || enableUseEventAPI) {976 beforeRemoveInstance(current.stateNode);977 }978 safelyDetachRef(current);979 return;980 }981 case HostPortal: {982 // TODO: this is recursive.983 // We are also not using this parent because984 // the portal will get pushed immediately.985 if (supportsMutation) {986 unmountHostComponents(finishedRoot, current, renderPriorityLevel);987 } else if (supportsPersistence) {988 emptyPortalContainer(current);989 }990 return;991 }992 case FundamentalComponent: {993 if (enableFundamentalAPI) {994 const fundamentalInstance = current.stateNode;995 if (fundamentalInstance !== null) {996 unmountFundamentalComponent(fundamentalInstance);997 current.stateNode = null;998 }999 }1000 return;1001 }1002 case DehydratedFragment: {1003 if (enableSuspenseCallback) {1004 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1005 if (hydrationCallbacks !== null) {1006 const onDeleted = hydrationCallbacks.onDeleted;1007 if (onDeleted) {1008 onDeleted((current.stateNode: SuspenseInstance));1009 }1010 }1011 }1012 return;1013 }1014 case ScopeComponent: {1015 if (enableDeprecatedFlareAPI) {1016 unmountDeprecatedResponderListeners(current);1017 }1018 if (enableScopeAPI) {1019 safelyDetachRef(current);1020 }1021 return;1022 }1023 }1024}1025function commitNestedUnmounts(1026 finishedRoot: FiberRoot,1027 root: Fiber,1028 renderPriorityLevel: ReactPriorityLevel,1029): void {1030 // While we're inside a removed host node we don't want to call1031 // removeChild on the inner nodes because they're removed by the top1032 // call anyway. We also want to call componentWillUnmount on all1033 // composites before this host node is removed from the tree. Therefore1034 // we do an inner loop while we're still inside the host node.1035 let node: Fiber = root;1036 while (true) {1037 commitUnmount(finishedRoot, node, renderPriorityLevel);1038 // Visit children because they may contain more composite or host nodes.1039 // Skip portals because commitUnmount() currently visits them recursively.1040 if (1041 node.child !== null &&1042 // If we use mutation we drill down into portals using commitUnmount above.1043 // If we don't use mutation we drill down into portals here instead.1044 (!supportsMutation || node.tag !== HostPortal)1045 ) {1046 node.child.return = node;1047 node = node.child;1048 continue;1049 }1050 if (node === root) {1051 return;1052 }1053 while (node.sibling === null) {1054 if (node.return === null || node.return === root) {1055 return;1056 }1057 node = node.return;1058 }1059 node.sibling.return = node.return;1060 node = node.sibling;1061 }1062}1063function detachFiber(current: Fiber) {1064 const alternate = current.alternate;1065 // Cut off the return pointers to disconnect it from the tree. Ideally, we1066 // should clear the child pointer of the parent alternate to let this1067 // get GC:ed but we don't know which for sure which parent is the current1068 // one so we'll settle for GC:ing the subtree of this child. This child1069 // itself will be GC:ed when the parent updates the next time.1070 current.return = null;1071 current.child = null;1072 current.memoizedState = null;1073 current.updateQueue = null;1074 current.dependencies = null;1075 current.alternate = null;1076 current.firstEffect = null;1077 current.lastEffect = null;1078 current.pendingProps = null;1079 current.memoizedProps = null;1080 current.stateNode = null;1081 if (alternate !== null) {1082 detachFiber(alternate);1083 }1084}1085function emptyPortalContainer(current: Fiber) {1086 if (!supportsPersistence) {1087 return;1088 }1089 const portal: {1090 containerInfo: Container,1091 pendingChildren: ChildSet,1092 ...1093 } = current.stateNode;1094 const {containerInfo} = portal;1095 const emptyChildSet = createContainerChildSet(containerInfo);1096 replaceContainerChildren(containerInfo, emptyChildSet);1097}1098function commitContainer(finishedWork: Fiber) {1099 if (!supportsPersistence) {1100 return;1101 }1102 switch (finishedWork.tag) {1103 case ClassComponent:1104 case HostComponent:1105 case HostText:1106 case FundamentalComponent: {1107 return;1108 }1109 case HostRoot:1110 case HostPortal: {1111 const portalOrRoot: {1112 containerInfo: Container,1113 pendingChildren: ChildSet,1114 ...1115 } = finishedWork.stateNode;1116 const {containerInfo, pendingChildren} = portalOrRoot;1117 replaceContainerChildren(containerInfo, pendingChildren);1118 return;1119 }1120 }1121 invariant(1122 false,1123 'This unit of work tag should not have side-effects. This error is ' +1124 'likely caused by a bug in React. Please file an issue.',1125 );1126}1127function getHostParentFiber(fiber: Fiber): Fiber {1128 let parent = fiber.return;1129 while (parent !== null) {1130 if (isHostParent(parent)) {1131 return parent;1132 }1133 parent = parent.return;1134 }1135 invariant(1136 false,1137 'Expected to find a host parent. This error is likely caused by a bug ' +1138 'in React. Please file an issue.',1139 );1140}1141function isHostParent(fiber: Fiber): boolean {1142 return (1143 fiber.tag === HostComponent ||1144 fiber.tag === HostRoot ||1145 fiber.tag === HostPortal1146 );1147}1148function getHostSibling(fiber: Fiber): ?Instance {1149 // We're going to search forward into the tree until we find a sibling host1150 // node. Unfortunately, if multiple insertions are done in a row we have to1151 // search past them. This leads to exponential search for the next sibling.1152 // TODO: Find a more efficient way to do this.1153 let node: Fiber = fiber;1154 siblings: while (true) {1155 // If we didn't find anything, let's try the next sibling.1156 while (node.sibling === null) {1157 if (node.return === null || isHostParent(node.return)) {1158 // If we pop out of the root or hit the parent the fiber we are the1159 // last sibling.1160 return null;1161 }1162 node = node.return;1163 }1164 node.sibling.return = node.return;1165 node = node.sibling;1166 while (1167 node.tag !== HostComponent &&1168 node.tag !== HostText &&1169 node.tag !== DehydratedFragment1170 ) {1171 // If it is not host node and, we might have a host node inside it.1172 // Try to search down until we find one.1173 if (node.effectTag & Placement) {1174 // If we don't have a child, try the siblings instead.1175 continue siblings;1176 }1177 // If we don't have a child, try the siblings instead.1178 // We also skip portals because they are not part of this host tree.1179 if (node.child === null || node.tag === HostPortal) {1180 continue siblings;1181 } else {1182 node.child.return = node;1183 node = node.child;1184 }1185 }1186 // Check if this host node is stable or about to be placed.1187 if (!(node.effectTag & Placement)) {1188 // Found it!1189 return node.stateNode;1190 }1191 }1192}1193function commitPlacement(finishedWork: Fiber): void {1194 if (!supportsMutation) {1195 return;1196 }1197 // Recursively insert all host nodes into the parent.1198 const parentFiber = getHostParentFiber(finishedWork);1199 // Note: these two variables *must* always be updated together.1200 let parent;1201 let isContainer;1202 const parentStateNode = parentFiber.stateNode;1203 switch (parentFiber.tag) {1204 case HostComponent:1205 parent = parentStateNode;1206 isContainer = false;1207 break;1208 case HostRoot:1209 parent = parentStateNode.containerInfo;1210 isContainer = true;1211 break;1212 case HostPortal:1213 parent = parentStateNode.containerInfo;1214 isContainer = true;1215 break;1216 case FundamentalComponent:1217 if (enableFundamentalAPI) {1218 parent = parentStateNode.instance;1219 isContainer = false;1220 }1221 // eslint-disable-next-line-no-fallthrough1222 default:1223 invariant(1224 false,1225 'Invalid host parent fiber. This error is likely caused by a bug ' +1226 'in React. Please file an issue.',1227 );1228 }1229 if (parentFiber.effectTag & ContentReset) {1230 // Reset the text content of the parent before doing any insertions1231 resetTextContent(parent);1232 // Clear ContentReset from the effect tag1233 parentFiber.effectTag &= ~ContentReset;1234 }1235 const before = getHostSibling(finishedWork);1236 // We only have the top Fiber that was inserted but we need to recurse down its1237 // children to find all the terminal nodes.1238 if (isContainer) {1239 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);1240 } else {1241 insertOrAppendPlacementNode(finishedWork, before, parent);1242 }1243}1244function insertOrAppendPlacementNodeIntoContainer(1245 node: Fiber,1246 before: ?Instance,1247 parent: Container,1248): void {1249 const {tag} = node;1250 const isHost = tag === HostComponent || tag === HostText;1251 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1252 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1253 if (before) {1254 insertInContainerBefore(parent, stateNode, before);1255 } else {1256 appendChildToContainer(parent, stateNode);1257 }1258 } else if (tag === HostPortal) {1259 // If the insertion itself is a portal, then we don't want to traverse1260 // down its children. Instead, we'll get insertions from each child in1261 // the portal directly.1262 } else {1263 const child = node.child;1264 if (child !== null) {1265 insertOrAppendPlacementNodeIntoContainer(child, before, parent);1266 let sibling = child.sibling;1267 while (sibling !== null) {1268 insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);1269 sibling = sibling.sibling;1270 }1271 }1272 }1273}1274function insertOrAppendPlacementNode(1275 node: Fiber,1276 before: ?Instance,1277 parent: Instance,1278): void {1279 const {tag} = node;1280 const isHost = tag === HostComponent || tag === HostText;1281 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1282 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1283 if (before) {1284 insertBefore(parent, stateNode, before);1285 } else {1286 appendChild(parent, stateNode);1287 }1288 } else if (tag === HostPortal) {1289 // If the insertion itself is a portal, then we don't want to traverse1290 // down its children. Instead, we'll get insertions from each child in1291 // the portal directly.1292 } else {1293 const child = node.child;1294 if (child !== null) {1295 insertOrAppendPlacementNode(child, before, parent);1296 let sibling = child.sibling;1297 while (sibling !== null) {1298 insertOrAppendPlacementNode(sibling, before, parent);1299 sibling = sibling.sibling;1300 }1301 }1302 }1303}1304function unmountHostComponents(1305 finishedRoot,1306 current,1307 renderPriorityLevel,1308): void {1309 // We only have the top Fiber that was deleted but we need to recurse down its1310 // children to find all the terminal nodes.1311 let node: Fiber = current;1312 // Each iteration, currentParent is populated with node's host parent if not1313 // currentParentIsValid.1314 let currentParentIsValid = false;1315 // Note: these two variables *must* always be updated together.1316 let currentParent;1317 let currentParentIsContainer;1318 while (true) {1319 if (!currentParentIsValid) {1320 let parent = node.return;1321 findParent: while (true) {1322 invariant(1323 parent !== null,1324 'Expected to find a host parent. This error is likely caused by ' +1325 'a bug in React. Please file an issue.',1326 );1327 const parentStateNode = parent.stateNode;1328 switch (parent.tag) {1329 case HostComponent:1330 currentParent = parentStateNode;1331 currentParentIsContainer = false;1332 break findParent;1333 case HostRoot:1334 currentParent = parentStateNode.containerInfo;1335 currentParentIsContainer = true;1336 break findParent;1337 case HostPortal:1338 currentParent = parentStateNode.containerInfo;1339 currentParentIsContainer = true;1340 break findParent;1341 case FundamentalComponent:1342 if (enableFundamentalAPI) {1343 currentParent = parentStateNode.instance;1344 currentParentIsContainer = false;1345 }1346 }1347 parent = parent.return;1348 }1349 currentParentIsValid = true;1350 }1351 if (node.tag === HostComponent || node.tag === HostText) {1352 commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);1353 // After all the children have unmounted, it is now safe to remove the1354 // node from the tree.1355 if (currentParentIsContainer) {1356 removeChildFromContainer(1357 ((currentParent: any): Container),1358 (node.stateNode: Instance | TextInstance),1359 );1360 } else {1361 removeChild(1362 ((currentParent: any): Instance),1363 (node.stateNode: Instance | TextInstance),1364 );1365 }1366 // Don't visit children because we already visited them.1367 } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {1368 const fundamentalNode = node.stateNode.instance;1369 commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);1370 // After all the children have unmounted, it is now safe to remove the1371 // node from the tree.1372 if (currentParentIsContainer) {1373 removeChildFromContainer(1374 ((currentParent: any): Container),1375 (fundamentalNode: Instance),1376 );1377 } else {1378 removeChild(1379 ((currentParent: any): Instance),1380 (fundamentalNode: Instance),1381 );1382 }1383 } else if (1384 enableSuspenseServerRenderer &&1385 node.tag === DehydratedFragment1386 ) {1387 if (enableSuspenseCallback) {1388 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1389 if (hydrationCallbacks !== null) {1390 const onDeleted = hydrationCallbacks.onDeleted;1391 if (onDeleted) {1392 onDeleted((node.stateNode: SuspenseInstance));1393 }1394 }1395 }1396 // Delete the dehydrated suspense boundary and all of its content.1397 if (currentParentIsContainer) {1398 clearSuspenseBoundaryFromContainer(1399 ((currentParent: any): Container),1400 (node.stateNode: SuspenseInstance),1401 );1402 } else {1403 clearSuspenseBoundary(1404 ((currentParent: any): Instance),1405 (node.stateNode: SuspenseInstance),1406 );1407 }1408 } else if (node.tag === HostPortal) {1409 if (node.child !== null) {1410 // When we go into a portal, it becomes the parent to remove from.1411 // We will reassign it back when we pop the portal on the way up.1412 currentParent = node.stateNode.containerInfo;1413 currentParentIsContainer = true;1414 // Visit children because portals might contain host components.1415 node.child.return = node;1416 node = node.child;1417 continue;1418 }1419 } else {1420 commitUnmount(finishedRoot, node, renderPriorityLevel);1421 // Visit children because we may find more host components below.1422 if (node.child !== null) {1423 node.child.return = node;1424 node = node.child;1425 continue;1426 }1427 }1428 if (node === current) {1429 return;1430 }1431 while (node.sibling === null) {1432 if (node.return === null || node.return === current) {1433 return;1434 }1435 node = node.return;1436 if (node.tag === HostPortal) {1437 // When we go out of the portal, we need to restore the parent.1438 // Since we don't keep a stack of them, we will search for it.1439 currentParentIsValid = false;1440 }1441 }1442 node.sibling.return = node.return;1443 node = node.sibling;1444 }1445}1446function commitDeletion(1447 finishedRoot: FiberRoot,1448 current: Fiber,1449 renderPriorityLevel: ReactPriorityLevel,1450): void {1451 if (supportsMutation) {1452 // Recursively delete all host nodes from the parent.1453 // Detach refs and call componentWillUnmount() on the whole subtree.1454 unmountHostComponents(finishedRoot, current, renderPriorityLevel);1455 } else {1456 // Detach refs and call componentWillUnmount() on the whole subtree.1457 commitNestedUnmounts(finishedRoot, current, renderPriorityLevel);1458 }1459 detachFiber(current);1460}1461function commitWork(current: Fiber | null, finishedWork: Fiber): void {1462 if (!supportsMutation) {1463 switch (finishedWork.tag) {1464 case FunctionComponent:1465 case ForwardRef:1466 case MemoComponent:1467 case SimpleMemoComponent:1468 case Block: {1469 // Layout effects are destroyed during the mutation phase so that all1470 // destroy functions for all fibers are called before any create functions.1471 // This prevents sibling component effects from interfering with each other,1472 // e.g. a destroy function in one component should never override a ref set1473 // by a create function in another component during the same commit.1474 if (1475 enableProfilerTimer &&1476 enableProfilerCommitHooks &&1477 finishedWork.mode & ProfileMode1478 ) {1479 try {1480 startLayoutEffectTimer();1481 commitHookEffectListUnmount(1482 HookLayout | HookHasEffect,1483 finishedWork,1484 );1485 } finally {1486 recordLayoutEffectDuration(finishedWork);1487 }1488 } else {1489 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1490 }1491 return;1492 }1493 case Profiler: {1494 return;1495 }1496 case SuspenseComponent: {1497 commitSuspenseComponent(finishedWork);1498 attachSuspenseRetryListeners(finishedWork);1499 return;1500 }1501 case SuspenseListComponent: {1502 attachSuspenseRetryListeners(finishedWork);1503 return;1504 }1505 case HostRoot: {1506 if (supportsHydration) {1507 const root: FiberRoot = finishedWork.stateNode;1508 if (root.hydrate) {1509 // We've just hydrated. No need to hydrate again.1510 root.hydrate = false;1511 commitHydratedContainer(root.containerInfo);1512 }1513 }1514 break;1515 }1516 }1517 commitContainer(finishedWork);1518 return;1519 }1520 switch (finishedWork.tag) {1521 case FunctionComponent:1522 case ForwardRef:1523 case MemoComponent:1524 case SimpleMemoComponent:1525 case Block: {1526 // Layout effects are destroyed during the mutation phase so that all1527 // destroy functions for all fibers are called before any create functions.1528 // This prevents sibling component effects from interfering with each other,1529 // e.g. a destroy function in one component should never override a ref set1530 // by a create function in another component during the same commit.1531 if (1532 enableProfilerTimer &&1533 enableProfilerCommitHooks &&1534 finishedWork.mode & ProfileMode1535 ) {1536 try {1537 startLayoutEffectTimer();1538 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1539 } finally {1540 recordLayoutEffectDuration(finishedWork);1541 }1542 } else {1543 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1544 }1545 return;1546 }1547 case ClassComponent: {1548 return;1549 }1550 case HostComponent: {1551 const instance: Instance = finishedWork.stateNode;1552 if (instance != null) {1553 // Commit the work prepared earlier.1554 const newProps = finishedWork.memoizedProps;1555 // For hydration we reuse the update path but we treat the oldProps1556 // as the newProps. The updatePayload will contain the real change in1557 // this case.1558 const oldProps = current !== null ? current.memoizedProps : newProps;1559 const type = finishedWork.type;1560 // TODO: Type the updateQueue to be specific to host components.1561 const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);1562 finishedWork.updateQueue = null;1563 if (updatePayload !== null) {1564 commitUpdate(1565 instance,1566 updatePayload,1567 type,1568 oldProps,1569 newProps,1570 finishedWork,1571 );1572 }1573 if (enableDeprecatedFlareAPI) {1574 const prevListeners = oldProps.DEPRECATED_flareListeners;1575 const nextListeners = newProps.DEPRECATED_flareListeners;1576 if (prevListeners !== nextListeners) {1577 updateDeprecatedEventListeners(nextListeners, finishedWork, null);1578 }1579 }1580 }1581 return;1582 }1583 case HostText: {1584 invariant(1585 finishedWork.stateNode !== null,1586 'This should have a text node initialized. This error is likely ' +1587 'caused by a bug in React. Please file an issue.',1588 );1589 const textInstance: TextInstance = finishedWork.stateNode;1590 const newText: string = finishedWork.memoizedProps;1591 // For hydration we reuse the update path but we treat the oldProps1592 // as the newProps. The updatePayload will contain the real change in1593 // this case.1594 const oldText: string =1595 current !== null ? current.memoizedProps : newText;1596 commitTextUpdate(textInstance, oldText, newText);1597 return;1598 }1599 case HostRoot: {1600 if (supportsHydration) {1601 const root: FiberRoot = finishedWork.stateNode;1602 if (root.hydrate) {1603 // We've just hydrated. No need to hydrate again.1604 root.hydrate = false;1605 commitHydratedContainer(root.containerInfo);1606 }1607 }1608 return;1609 }1610 case Profiler: {1611 return;1612 }1613 case SuspenseComponent: {1614 commitSuspenseComponent(finishedWork);1615 attachSuspenseRetryListeners(finishedWork);1616 return;1617 }1618 case SuspenseListComponent: {1619 attachSuspenseRetryListeners(finishedWork);1620 return;1621 }1622 case IncompleteClassComponent: {1623 return;1624 }1625 case FundamentalComponent: {1626 if (enableFundamentalAPI) {1627 const fundamentalInstance = finishedWork.stateNode;1628 updateFundamentalComponent(fundamentalInstance);1629 return;1630 }1631 break;1632 }1633 case ScopeComponent: {1634 if (enableScopeAPI) {1635 const scopeInstance = finishedWork.stateNode;1636 scopeInstance.fiber = finishedWork;1637 if (enableDeprecatedFlareAPI) {1638 const newProps = finishedWork.memoizedProps;1639 const oldProps = current !== null ? current.memoizedProps : newProps;1640 const prevListeners = oldProps.DEPRECATED_flareListeners;1641 const nextListeners = newProps.DEPRECATED_flareListeners;1642 if (prevListeners !== nextListeners || current === null) {1643 updateDeprecatedEventListeners(nextListeners, finishedWork, null);1644 }1645 }1646 return;1647 }1648 break;1649 }1650 }1651 invariant(1652 false,1653 'This unit of work tag should not have side-effects. This error is ' +1654 'likely caused by a bug in React. Please file an issue.',1655 );1656}1657function commitSuspenseComponent(finishedWork: Fiber) {1658 const newState: SuspenseState | null = finishedWork.memoizedState;1659 let newDidTimeout;1660 let primaryChildParent = finishedWork;1661 if (newState === null) {1662 newDidTimeout = false;1663 } else {1664 newDidTimeout = true;1665 primaryChildParent = finishedWork.child;1666 markCommitTimeOfFallback();1667 }1668 if (supportsMutation && primaryChildParent !== null) {1669 hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);1670 }1671 if (enableSuspenseCallback && newState !== null) {1672 const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;1673 if (typeof suspenseCallback === 'function') {1674 const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);1675 if (wakeables !== null) {1676 suspenseCallback(new Set(wakeables));1677 }1678 } else if (__DEV__) {1679 if (suspenseCallback !== undefined) {1680 console.error('Unexpected type for suspenseCallback.');1681 }1682 }1683 }1684}1685function commitSuspenseHydrationCallbacks(1686 finishedRoot: FiberRoot,1687 finishedWork: Fiber,1688) {1689 if (!supportsHydration) {1690 return;1691 }1692 const newState: SuspenseState | null = finishedWork.memoizedState;1693 if (newState === null) {1694 const current = finishedWork.alternate;1695 if (current !== null) {1696 const prevState: SuspenseState | null = current.memoizedState;1697 if (prevState !== null) {1698 const suspenseInstance = prevState.dehydrated;1699 if (suspenseInstance !== null) {1700 commitHydratedSuspenseInstance(suspenseInstance);1701 if (enableSuspenseCallback) {1702 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1703 if (hydrationCallbacks !== null) {1704 const onHydrated = hydrationCallbacks.onHydrated;1705 if (onHydrated) {1706 onHydrated(suspenseInstance);1707 }1708 }1709 }1710 }1711 }1712 }1713 }1714}1715function attachSuspenseRetryListeners(finishedWork: Fiber) {1716 // If this boundary just timed out, then it will have a set of wakeables.1717 // For each wakeable, attach a listener so that when it resolves, React1718 // attempts to re-render the boundary in the primary (pre-timeout) state.1719 const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);1720 if (wakeables !== null) {1721 finishedWork.updateQueue = null;1722 let retryCache = finishedWork.stateNode;1723 if (retryCache === null) {1724 retryCache = finishedWork.stateNode = new PossiblyWeakSet();1725 }1726 wakeables.forEach(wakeable => {1727 // Memoize using the boundary fiber to prevent redundant listeners.1728 let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);1729 if (!retryCache.has(wakeable)) {1730 if (enableSchedulerTracing) {1731 if (wakeable.__reactDoNotTraceInteractions !== true) {1732 retry = Schedule_tracing_wrap(retry);1733 }1734 }1735 retryCache.add(wakeable);1736 wakeable.then(retry, retry);1737 }1738 });1739 }1740}1741function commitResetTextContent(current: Fiber) {1742 if (!supportsMutation) {1743 return;1744 }1745 resetTextContent(current.stateNode);1746}1747export {1748 commitBeforeMutationLifeCycles,1749 commitResetTextContent,1750 commitPlacement,1751 commitDeletion,1752 commitWork,1753 commitLifeCycles,1754 commitAttachRef,1755 commitDetachRef,...
ReactFiberCommitWork.new.js
Source:ReactFiberCommitWork.new.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */9import type {10 Instance,11 TextInstance,12 SuspenseInstance,13 Container,14 ChildSet,15 UpdatePayload,16} from './ReactFiberHostConfig';17import type {Fiber} from './ReactInternalTypes';18import type {FiberRoot} from './ReactInternalTypes';19import type {Lanes} from './ReactFiberLane.new';20import type {SuspenseState} from './ReactFiberSuspenseComponent.new';21import type {UpdateQueue} from './ReactUpdateQueue.new';22import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';23import type {Wakeable} from 'shared/ReactTypes';24import type {ReactPriorityLevel} from './ReactInternalTypes';25import type {OffscreenState} from './ReactFiberOffscreenComponent';26import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';27import {28 enableSchedulerTracing,29 enableProfilerTimer,30 enableProfilerCommitHooks,31 enableProfilerNestedUpdatePhase,32 enableSuspenseServerRenderer,33 enableFundamentalAPI,34 enableSuspenseCallback,35 enableScopeAPI,36} from 'shared/ReactFeatureFlags';37import {38 FunctionComponent,39 ForwardRef,40 ClassComponent,41 HostRoot,42 HostComponent,43 HostText,44 HostPortal,45 Profiler,46 SuspenseComponent,47 DehydratedFragment,48 IncompleteClassComponent,49 MemoComponent,50 SimpleMemoComponent,51 SuspenseListComponent,52 FundamentalComponent,53 ScopeComponent,54 OffscreenComponent,55 LegacyHiddenComponent,56} from './ReactWorkTags';57import {58 invokeGuardedCallback,59 hasCaughtError,60 clearCaughtError,61} from 'shared/ReactErrorUtils';62import {63 NoFlags,64 ContentReset,65 Placement,66 Snapshot,67 Update,68} from './ReactFiberFlags';69import getComponentName from 'shared/getComponentName';70import invariant from 'shared/invariant';71import {onCommitUnmount} from './ReactFiberDevToolsHook.new';72import {resolveDefaultProps} from './ReactFiberLazyComponent.new';73import {74 isCurrentUpdateNested,75 getCommitTime,76 recordLayoutEffectDuration,77 startLayoutEffectTimer,78} from './ReactProfilerTimer.new';79import {ProfileMode} from './ReactTypeOfMode';80import {commitUpdateQueue} from './ReactUpdateQueue.new';81import {82 getPublicInstance,83 supportsMutation,84 supportsPersistence,85 supportsHydration,86 commitMount,87 commitUpdate,88 resetTextContent,89 commitTextUpdate,90 appendChild,91 appendChildToContainer,92 insertBefore,93 insertInContainerBefore,94 removeChild,95 removeChildFromContainer,96 clearSuspenseBoundary,97 clearSuspenseBoundaryFromContainer,98 replaceContainerChildren,99 createContainerChildSet,100 hideInstance,101 hideTextInstance,102 unhideInstance,103 unhideTextInstance,104 unmountFundamentalComponent,105 updateFundamentalComponent,106 commitHydratedContainer,107 commitHydratedSuspenseInstance,108 clearContainer,109 prepareScopeUpdate,110} from './ReactFiberHostConfig';111import {112 captureCommitPhaseError,113 resolveRetryWakeable,114 markCommitTimeOfFallback,115 enqueuePendingPassiveHookEffectMount,116 enqueuePendingPassiveHookEffectUnmount,117 enqueuePendingPassiveProfilerEffect,118} from './ReactFiberWorkLoop.new';119import {120 NoFlags as NoHookEffect,121 HasEffect as HookHasEffect,122 Layout as HookLayout,123 Passive as HookPassive,124} from './ReactHookEffectTags';125import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new';126let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;127if (__DEV__) {128 didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();129}130const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;131const callComponentWillUnmountWithTimer = function(current, instance) {132 instance.props = current.memoizedProps;133 instance.state = current.memoizedState;134 if (135 enableProfilerTimer &&136 enableProfilerCommitHooks &&137 current.mode & ProfileMode138 ) {139 try {140 startLayoutEffectTimer();141 instance.componentWillUnmount();142 } finally {143 recordLayoutEffectDuration(current);144 }145 } else {146 instance.componentWillUnmount();147 }148};149// Capture errors so they don't interrupt unmounting.150function safelyCallComponentWillUnmount(current: Fiber, instance: any) {151 if (__DEV__) {152 invokeGuardedCallback(153 null,154 callComponentWillUnmountWithTimer,155 null,156 current,157 instance,158 );159 if (hasCaughtError()) {160 const unmountError = clearCaughtError();161 captureCommitPhaseError(current, unmountError);162 }163 } else {164 try {165 callComponentWillUnmountWithTimer(current, instance);166 } catch (unmountError) {167 captureCommitPhaseError(current, unmountError);168 }169 }170}171function safelyDetachRef(current: Fiber) {172 const ref = current.ref;173 if (ref !== null) {174 if (typeof ref === 'function') {175 if (__DEV__) {176 if (177 enableProfilerTimer &&178 enableProfilerCommitHooks &&179 current.mode & ProfileMode180 ) {181 startLayoutEffectTimer();182 invokeGuardedCallback(null, ref, null, null);183 recordLayoutEffectDuration(current);184 } else {185 invokeGuardedCallback(null, ref, null, null);186 }187 if (hasCaughtError()) {188 const refError = clearCaughtError();189 captureCommitPhaseError(current, refError);190 }191 } else {192 try {193 if (194 enableProfilerTimer &&195 enableProfilerCommitHooks &&196 current.mode & ProfileMode197 ) {198 try {199 startLayoutEffectTimer();200 ref(null);201 } finally {202 recordLayoutEffectDuration(current);203 }204 } else {205 ref(null);206 }207 } catch (refError) {208 captureCommitPhaseError(current, refError);209 }210 }211 } else {212 ref.current = null;213 }214 }215}216function safelyCallDestroy(current: Fiber, destroy: () => void) {217 if (__DEV__) {218 invokeGuardedCallback(null, destroy, null);219 if (hasCaughtError()) {220 const error = clearCaughtError();221 captureCommitPhaseError(current, error);222 }223 } else {224 try {225 destroy();226 } catch (error) {227 captureCommitPhaseError(current, error);228 }229 }230}231function commitBeforeMutationLifeCycles(232 current: Fiber | null,233 finishedWork: Fiber,234): void {235 switch (finishedWork.tag) {236 case FunctionComponent:237 case ForwardRef:238 case SimpleMemoComponent: {239 return;240 }241 case ClassComponent: {242 if (finishedWork.flags & Snapshot) {243 if (current !== null) {244 const prevProps = current.memoizedProps;245 const prevState = current.memoizedState;246 const instance = finishedWork.stateNode;247 // We could update instance props and state here,248 // but instead we rely on them being set during last render.249 // TODO: revisit this when we implement resuming.250 if (__DEV__) {251 if (252 finishedWork.type === finishedWork.elementType &&253 !didWarnAboutReassigningProps254 ) {255 if (instance.props !== finishedWork.memoizedProps) {256 console.error(257 'Expected %s props to match memoized props before ' +258 'getSnapshotBeforeUpdate. ' +259 'This might either be because of a bug in React, or because ' +260 'a component reassigns its own `this.props`. ' +261 'Please file an issue.',262 getComponentName(finishedWork.type) || 'instance',263 );264 }265 if (instance.state !== finishedWork.memoizedState) {266 console.error(267 'Expected %s state to match memoized state before ' +268 'getSnapshotBeforeUpdate. ' +269 'This might either be because of a bug in React, or because ' +270 'a component reassigns its own `this.state`. ' +271 'Please file an issue.',272 getComponentName(finishedWork.type) || 'instance',273 );274 }275 }276 }277 const snapshot = instance.getSnapshotBeforeUpdate(278 finishedWork.elementType === finishedWork.type279 ? prevProps280 : resolveDefaultProps(finishedWork.type, prevProps),281 prevState,282 );283 if (__DEV__) {284 const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);285 if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {286 didWarnSet.add(finishedWork.type);287 console.error(288 '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +289 'must be returned. You have returned undefined.',290 getComponentName(finishedWork.type),291 );292 }293 }294 instance.__reactInternalSnapshotBeforeUpdate = snapshot;295 }296 }297 return;298 }299 case HostRoot: {300 if (supportsMutation) {301 if (finishedWork.flags & Snapshot) {302 const root = finishedWork.stateNode;303 clearContainer(root.containerInfo);304 }305 }306 return;307 }308 case HostComponent:309 case HostText:310 case HostPortal:311 case IncompleteClassComponent:312 // Nothing to do for these component types313 return;314 }315 invariant(316 false,317 'This unit of work tag should not have side-effects. This error is ' +318 'likely caused by a bug in React. Please file an issue.',319 );320}321function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {322 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);323 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;324 if (lastEffect !== null) {325 const firstEffect = lastEffect.next;326 let effect = firstEffect;327 do {328 if ((effect.tag & tag) === tag) {329 // Unmount330 const destroy = effect.destroy;331 effect.destroy = undefined;332 if (destroy !== undefined) {333 destroy();334 }335 }336 effect = effect.next;337 } while (effect !== firstEffect);338 }339}340function commitHookEffectListMount(tag: number, finishedWork: Fiber) {341 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);342 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;343 if (lastEffect !== null) {344 const firstEffect = lastEffect.next;345 let effect = firstEffect;346 do {347 if ((effect.tag & tag) === tag) {348 // Mount349 const create = effect.create;350 effect.destroy = create();351 if (__DEV__) {352 const destroy = effect.destroy;353 if (destroy !== undefined && typeof destroy !== 'function') {354 let addendum;355 if (destroy === null) {356 addendum =357 ' You returned null. If your effect does not require clean ' +358 'up, return undefined (or nothing).';359 } else if (typeof destroy.then === 'function') {360 addendum =361 '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +362 'Instead, write the async function inside your effect ' +363 'and call it immediately:\n\n' +364 'useEffect(() => {\n' +365 ' async function fetchData() {\n' +366 ' // You can await here\n' +367 ' const response = await MyAPI.getData(someId);\n' +368 ' // ...\n' +369 ' }\n' +370 ' fetchData();\n' +371 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +372 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';373 } else {374 addendum = ' You returned: ' + destroy;375 }376 console.error(377 'An effect function must not return anything besides a function, ' +378 'which is used for clean-up.%s',379 addendum,380 );381 }382 }383 }384 effect = effect.next;385 } while (effect !== firstEffect);386 }387}388function schedulePassiveEffects(finishedWork: Fiber) {389 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);390 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;391 if (lastEffect !== null) {392 const firstEffect = lastEffect.next;393 let effect = firstEffect;394 do {395 const {next, tag} = effect;396 if (397 (tag & HookPassive) !== NoHookEffect &&398 (tag & HookHasEffect) !== NoHookEffect399 ) {400 enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);401 enqueuePendingPassiveHookEffectMount(finishedWork, effect);402 }403 effect = next;404 } while (effect !== firstEffect);405 }406}407export function commitPassiveEffectDurations(408 finishedRoot: FiberRoot,409 finishedWork: Fiber,410): void {411 if (enableProfilerTimer && enableProfilerCommitHooks) {412 // Only Profilers with work in their subtree will have an Update effect scheduled.413 if ((finishedWork.flags & Update) !== NoFlags) {414 switch (finishedWork.tag) {415 case Profiler: {416 const {passiveEffectDuration} = finishedWork.stateNode;417 const {id, onPostCommit} = finishedWork.memoizedProps;418 // This value will still reflect the previous commit phase.419 // It does not get reset until the start of the next commit phase.420 const commitTime = getCommitTime();421 let phase = finishedWork.alternate === null ? 'mount' : 'update';422 if (enableProfilerNestedUpdatePhase) {423 if (isCurrentUpdateNested()) {424 phase = 'nested-update';425 }426 }427 if (typeof onPostCommit === 'function') {428 if (enableSchedulerTracing) {429 onPostCommit(430 id,431 phase,432 passiveEffectDuration,433 commitTime,434 finishedRoot.memoizedInteractions,435 );436 } else {437 onPostCommit(id, phase, passiveEffectDuration, commitTime);438 }439 }440 // Bubble times to the next nearest ancestor Profiler.441 // After we process that Profiler, we'll bubble further up.442 let parentFiber = finishedWork.return;443 while (parentFiber !== null) {444 if (parentFiber.tag === Profiler) {445 const parentStateNode = parentFiber.stateNode;446 parentStateNode.passiveEffectDuration += passiveEffectDuration;447 break;448 }449 parentFiber = parentFiber.return;450 }451 break;452 }453 default:454 break;455 }456 }457 }458}459function commitLifeCycles(460 finishedRoot: FiberRoot,461 current: Fiber | null,462 finishedWork: Fiber,463 committedLanes: Lanes,464): void {465 switch (finishedWork.tag) {466 case FunctionComponent:467 case ForwardRef:468 case SimpleMemoComponent: {469 // At this point layout effects have already been destroyed (during mutation phase).470 // This is done to prevent sibling component effects from interfering with each other,471 // e.g. a destroy function in one component should never override a ref set472 // by a create function in another component during the same commit.473 if (474 enableProfilerTimer &&475 enableProfilerCommitHooks &&476 finishedWork.mode & ProfileMode477 ) {478 try {479 startLayoutEffectTimer();480 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);481 } finally {482 recordLayoutEffectDuration(finishedWork);483 }484 } else {485 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);486 }487 schedulePassiveEffects(finishedWork);488 return;489 }490 case ClassComponent: {491 const instance = finishedWork.stateNode;492 if (finishedWork.flags & Update) {493 if (current === null) {494 // We could update instance props and state here,495 // but instead we rely on them being set during last render.496 // TODO: revisit this when we implement resuming.497 if (__DEV__) {498 if (499 finishedWork.type === finishedWork.elementType &&500 !didWarnAboutReassigningProps501 ) {502 if (instance.props !== finishedWork.memoizedProps) {503 console.error(504 'Expected %s props to match memoized props before ' +505 'componentDidMount. ' +506 'This might either be because of a bug in React, or because ' +507 'a component reassigns its own `this.props`. ' +508 'Please file an issue.',509 getComponentName(finishedWork.type) || 'instance',510 );511 }512 if (instance.state !== finishedWork.memoizedState) {513 console.error(514 'Expected %s state to match memoized state before ' +515 'componentDidMount. ' +516 'This might either be because of a bug in React, or because ' +517 'a component reassigns its own `this.state`. ' +518 'Please file an issue.',519 getComponentName(finishedWork.type) || 'instance',520 );521 }522 }523 }524 if (525 enableProfilerTimer &&526 enableProfilerCommitHooks &&527 finishedWork.mode & ProfileMode528 ) {529 try {530 startLayoutEffectTimer();531 instance.componentDidMount();532 } finally {533 recordLayoutEffectDuration(finishedWork);534 }535 } else {536 instance.componentDidMount();537 }538 } else {539 const prevProps =540 finishedWork.elementType === finishedWork.type541 ? current.memoizedProps542 : resolveDefaultProps(finishedWork.type, current.memoizedProps);543 const prevState = current.memoizedState;544 // We could update instance props and state here,545 // but instead we rely on them being set during last render.546 // TODO: revisit this when we implement resuming.547 if (__DEV__) {548 if (549 finishedWork.type === finishedWork.elementType &&550 !didWarnAboutReassigningProps551 ) {552 if (instance.props !== finishedWork.memoizedProps) {553 console.error(554 'Expected %s props to match memoized props before ' +555 'componentDidUpdate. ' +556 'This might either be because of a bug in React, or because ' +557 'a component reassigns its own `this.props`. ' +558 'Please file an issue.',559 getComponentName(finishedWork.type) || 'instance',560 );561 }562 if (instance.state !== finishedWork.memoizedState) {563 console.error(564 'Expected %s state to match memoized state before ' +565 'componentDidUpdate. ' +566 'This might either be because of a bug in React, or because ' +567 'a component reassigns its own `this.state`. ' +568 'Please file an issue.',569 getComponentName(finishedWork.type) || 'instance',570 );571 }572 }573 }574 if (575 enableProfilerTimer &&576 enableProfilerCommitHooks &&577 finishedWork.mode & ProfileMode578 ) {579 try {580 startLayoutEffectTimer();581 instance.componentDidUpdate(582 prevProps,583 prevState,584 instance.__reactInternalSnapshotBeforeUpdate,585 );586 } finally {587 recordLayoutEffectDuration(finishedWork);588 }589 } else {590 instance.componentDidUpdate(591 prevProps,592 prevState,593 instance.__reactInternalSnapshotBeforeUpdate,594 );595 }596 }597 }598 // TODO: I think this is now always non-null by the time it reaches the599 // commit phase. Consider removing the type check.600 const updateQueue: UpdateQueue<601 *,602 > | null = (finishedWork.updateQueue: any);603 if (updateQueue !== null) {604 if (__DEV__) {605 if (606 finishedWork.type === finishedWork.elementType &&607 !didWarnAboutReassigningProps608 ) {609 if (instance.props !== finishedWork.memoizedProps) {610 console.error(611 'Expected %s props to match memoized props before ' +612 'processing the update queue. ' +613 'This might either be because of a bug in React, or because ' +614 'a component reassigns its own `this.props`. ' +615 'Please file an issue.',616 getComponentName(finishedWork.type) || 'instance',617 );618 }619 if (instance.state !== finishedWork.memoizedState) {620 console.error(621 'Expected %s state to match memoized state before ' +622 'processing the update queue. ' +623 'This might either be because of a bug in React, or because ' +624 'a component reassigns its own `this.state`. ' +625 'Please file an issue.',626 getComponentName(finishedWork.type) || 'instance',627 );628 }629 }630 }631 // We could update instance props and state here,632 // but instead we rely on them being set during last render.633 // TODO: revisit this when we implement resuming.634 commitUpdateQueue(finishedWork, updateQueue, instance);635 }636 return;637 }638 case HostRoot: {639 // TODO: I think this is now always non-null by the time it reaches the640 // commit phase. Consider removing the type check.641 const updateQueue: UpdateQueue<642 *,643 > | null = (finishedWork.updateQueue: any);644 if (updateQueue !== null) {645 let instance = null;646 if (finishedWork.child !== null) {647 switch (finishedWork.child.tag) {648 case HostComponent:649 instance = getPublicInstance(finishedWork.child.stateNode);650 break;651 case ClassComponent:652 instance = finishedWork.child.stateNode;653 break;654 }655 }656 commitUpdateQueue(finishedWork, updateQueue, instance);657 }658 return;659 }660 case HostComponent: {661 const instance: Instance = finishedWork.stateNode;662 // Renderers may schedule work to be done after host components are mounted663 // (eg DOM renderer may schedule auto-focus for inputs and form controls).664 // These effects should only be committed when components are first mounted,665 // aka when there is no current/alternate.666 if (current === null && finishedWork.flags & Update) {667 const type = finishedWork.type;668 const props = finishedWork.memoizedProps;669 commitMount(instance, type, props, finishedWork);670 }671 return;672 }673 case HostText: {674 // We have no life-cycles associated with text.675 return;676 }677 case HostPortal: {678 // We have no life-cycles associated with portals.679 return;680 }681 case Profiler: {682 if (enableProfilerTimer) {683 const {onCommit, onRender} = finishedWork.memoizedProps;684 const {effectDuration} = finishedWork.stateNode;685 const commitTime = getCommitTime();686 let phase = current === null ? 'mount' : 'update';687 if (enableProfilerNestedUpdatePhase) {688 if (isCurrentUpdateNested()) {689 phase = 'nested-update';690 }691 }692 if (typeof onRender === 'function') {693 if (enableSchedulerTracing) {694 onRender(695 finishedWork.memoizedProps.id,696 phase,697 finishedWork.actualDuration,698 finishedWork.treeBaseDuration,699 finishedWork.actualStartTime,700 commitTime,701 finishedRoot.memoizedInteractions,702 );703 } else {704 onRender(705 finishedWork.memoizedProps.id,706 phase,707 finishedWork.actualDuration,708 finishedWork.treeBaseDuration,709 finishedWork.actualStartTime,710 commitTime,711 );712 }713 }714 if (enableProfilerCommitHooks) {715 if (typeof onCommit === 'function') {716 if (enableSchedulerTracing) {717 onCommit(718 finishedWork.memoizedProps.id,719 phase,720 effectDuration,721 commitTime,722 finishedRoot.memoizedInteractions,723 );724 } else {725 onCommit(726 finishedWork.memoizedProps.id,727 phase,728 effectDuration,729 commitTime,730 );731 }732 }733 // Schedule a passive effect for this Profiler to call onPostCommit hooks.734 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,735 // because the effect is also where times bubble to parent Profilers.736 enqueuePendingPassiveProfilerEffect(finishedWork);737 // Propagate layout effect durations to the next nearest Profiler ancestor.738 // Do not reset these values until the next render so DevTools has a chance to read them first.739 let parentFiber = finishedWork.return;740 while (parentFiber !== null) {741 if (parentFiber.tag === Profiler) {742 const parentStateNode = parentFiber.stateNode;743 parentStateNode.effectDuration += effectDuration;744 break;745 }746 parentFiber = parentFiber.return;747 }748 }749 }750 return;751 }752 case SuspenseComponent: {753 commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);754 return;755 }756 case SuspenseListComponent:757 case IncompleteClassComponent:758 case FundamentalComponent:759 case ScopeComponent:760 case OffscreenComponent:761 case LegacyHiddenComponent:762 return;763 }764 invariant(765 false,766 'This unit of work tag should not have side-effects. This error is ' +767 'likely caused by a bug in React. Please file an issue.',768 );769}770function hideOrUnhideAllChildren(finishedWork, isHidden) {771 if (supportsMutation) {772 // We only have the top Fiber that was inserted but we need to recurse down its773 // children to find all the terminal nodes.774 let node: Fiber = finishedWork;775 while (true) {776 if (node.tag === HostComponent) {777 const instance = node.stateNode;778 if (isHidden) {779 hideInstance(instance);780 } else {781 unhideInstance(node.stateNode, node.memoizedProps);782 }783 } else if (node.tag === HostText) {784 const instance = node.stateNode;785 if (isHidden) {786 hideTextInstance(instance);787 } else {788 unhideTextInstance(instance, node.memoizedProps);789 }790 } else if (791 (node.tag === OffscreenComponent ||792 node.tag === LegacyHiddenComponent) &&793 (node.memoizedState: OffscreenState) !== null &&794 node !== finishedWork795 ) {796 // Found a nested Offscreen component that is hidden. Don't search797 // any deeper. This tree should remain hidden.798 } else if (node.child !== null) {799 node.child.return = node;800 node = node.child;801 continue;802 }803 if (node === finishedWork) {804 return;805 }806 while (node.sibling === null) {807 if (node.return === null || node.return === finishedWork) {808 return;809 }810 node = node.return;811 }812 node.sibling.return = node.return;813 node = node.sibling;814 }815 }816}817function commitAttachRef(finishedWork: Fiber) {818 const ref = finishedWork.ref;819 if (ref !== null) {820 const instance = finishedWork.stateNode;821 let instanceToUse;822 switch (finishedWork.tag) {823 case HostComponent:824 instanceToUse = getPublicInstance(instance);825 break;826 default:827 instanceToUse = instance;828 }829 // Moved outside to ensure DCE works with this flag830 if (enableScopeAPI && finishedWork.tag === ScopeComponent) {831 instanceToUse = instance;832 }833 if (typeof ref === 'function') {834 if (835 enableProfilerTimer &&836 enableProfilerCommitHooks &&837 finishedWork.mode & ProfileMode838 ) {839 try {840 startLayoutEffectTimer();841 ref(instanceToUse);842 } finally {843 recordLayoutEffectDuration(finishedWork);844 }845 } else {846 ref(instanceToUse);847 }848 } else {849 if (__DEV__) {850 if (!ref.hasOwnProperty('current')) {851 console.error(852 'Unexpected ref object provided for %s. ' +853 'Use either a ref-setter function or React.createRef().',854 getComponentName(finishedWork.type),855 );856 }857 }858 ref.current = instanceToUse;859 }860 }861}862function commitDetachRef(current: Fiber) {863 const currentRef = current.ref;864 if (currentRef !== null) {865 if (typeof currentRef === 'function') {866 if (867 enableProfilerTimer &&868 enableProfilerCommitHooks &&869 current.mode & ProfileMode870 ) {871 try {872 startLayoutEffectTimer();873 currentRef(null);874 } finally {875 recordLayoutEffectDuration(current);876 }877 } else {878 currentRef(null);879 }880 } else {881 currentRef.current = null;882 }883 }884}885// User-originating errors (lifecycles and refs) should not interrupt886// deletion, so don't let them throw. Host-originating errors should887// interrupt deletion, so it's okay888function commitUnmount(889 finishedRoot: FiberRoot,890 current: Fiber,891 renderPriorityLevel: ReactPriorityLevel,892): void {893 onCommitUnmount(current);894 switch (current.tag) {895 case FunctionComponent:896 case ForwardRef:897 case MemoComponent:898 case SimpleMemoComponent: {899 const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);900 if (updateQueue !== null) {901 const lastEffect = updateQueue.lastEffect;902 if (lastEffect !== null) {903 const firstEffect = lastEffect.next;904 let effect = firstEffect;905 do {906 const {destroy, tag} = effect;907 if (destroy !== undefined) {908 if ((tag & HookPassive) !== NoHookEffect) {909 enqueuePendingPassiveHookEffectUnmount(current, effect);910 } else {911 if (912 enableProfilerTimer &&913 enableProfilerCommitHooks &&914 current.mode & ProfileMode915 ) {916 startLayoutEffectTimer();917 safelyCallDestroy(current, destroy);918 recordLayoutEffectDuration(current);919 } else {920 safelyCallDestroy(current, destroy);921 }922 }923 }924 effect = effect.next;925 } while (effect !== firstEffect);926 }927 }928 return;929 }930 case ClassComponent: {931 safelyDetachRef(current);932 const instance = current.stateNode;933 if (typeof instance.componentWillUnmount === 'function') {934 safelyCallComponentWillUnmount(current, instance);935 }936 return;937 }938 case HostComponent: {939 safelyDetachRef(current);940 return;941 }942 case HostPortal: {943 // TODO: this is recursive.944 // We are also not using this parent because945 // the portal will get pushed immediately.946 if (supportsMutation) {947 unmountHostComponents(finishedRoot, current, renderPriorityLevel);948 } else if (supportsPersistence) {949 emptyPortalContainer(current);950 }951 return;952 }953 case FundamentalComponent: {954 if (enableFundamentalAPI) {955 const fundamentalInstance = current.stateNode;956 if (fundamentalInstance !== null) {957 unmountFundamentalComponent(fundamentalInstance);958 current.stateNode = null;959 }960 }961 return;962 }963 case DehydratedFragment: {964 if (enableSuspenseCallback) {965 const hydrationCallbacks = finishedRoot.hydrationCallbacks;966 if (hydrationCallbacks !== null) {967 const onDeleted = hydrationCallbacks.onDeleted;968 if (onDeleted) {969 onDeleted((current.stateNode: SuspenseInstance));970 }971 }972 }973 return;974 }975 case ScopeComponent: {976 if (enableScopeAPI) {977 safelyDetachRef(current);978 }979 return;980 }981 }982}983function commitNestedUnmounts(984 finishedRoot: FiberRoot,985 root: Fiber,986 renderPriorityLevel: ReactPriorityLevel,987): void {988 // While we're inside a removed host node we don't want to call989 // removeChild on the inner nodes because they're removed by the top990 // call anyway. We also want to call componentWillUnmount on all991 // composites before this host node is removed from the tree. Therefore992 // we do an inner loop while we're still inside the host node.993 let node: Fiber = root;994 while (true) {995 commitUnmount(finishedRoot, node, renderPriorityLevel);996 // Visit children because they may contain more composite or host nodes.997 // Skip portals because commitUnmount() currently visits them recursively.998 if (999 node.child !== null &&1000 // If we use mutation we drill down into portals using commitUnmount above.1001 // If we don't use mutation we drill down into portals here instead.1002 (!supportsMutation || node.tag !== HostPortal)1003 ) {1004 node.child.return = node;1005 node = node.child;1006 continue;1007 }1008 if (node === root) {1009 return;1010 }1011 while (node.sibling === null) {1012 if (node.return === null || node.return === root) {1013 return;1014 }1015 node = node.return;1016 }1017 node.sibling.return = node.return;1018 node = node.sibling;1019 }1020}1021function detachFiberMutation(fiber: Fiber) {1022 // Cut off the return pointers to disconnect it from the tree. Ideally, we1023 // should clear the child pointer of the parent alternate to let this1024 // get GC:ed but we don't know which for sure which parent is the current1025 // one so we'll settle for GC:ing the subtree of this child. This child1026 // itself will be GC:ed when the parent updates the next time.1027 // Note: we cannot null out sibling here, otherwise it can cause issues1028 // with findDOMNode and how it requires the sibling field to carry out1029 // traversal in a later effect. See PR #16820. We now clear the sibling1030 // field after effects, see: detachFiberAfterEffects.1031 //1032 // Don't disconnect stateNode now; it will be detached in detachFiberAfterEffects.1033 // It may be required if the current component is an error boundary,1034 // and one of its descendants throws while unmounting a passive effect.1035 fiber.alternate = null;1036 fiber.child = null;1037 fiber.dependencies = null;1038 fiber.firstEffect = null;1039 fiber.lastEffect = null;1040 fiber.memoizedProps = null;1041 fiber.memoizedState = null;1042 fiber.pendingProps = null;1043 fiber.return = null;1044 fiber.updateQueue = null;1045 if (__DEV__) {1046 fiber._debugOwner = null;1047 }1048}1049function emptyPortalContainer(current: Fiber) {1050 if (!supportsPersistence) {1051 return;1052 }1053 const portal: {1054 containerInfo: Container,1055 pendingChildren: ChildSet,1056 ...1057 } = current.stateNode;1058 const {containerInfo} = portal;1059 const emptyChildSet = createContainerChildSet(containerInfo);1060 replaceContainerChildren(containerInfo, emptyChildSet);1061}1062function commitContainer(finishedWork: Fiber) {1063 if (!supportsPersistence) {1064 return;1065 }1066 switch (finishedWork.tag) {1067 case ClassComponent:1068 case HostComponent:1069 case HostText:1070 case FundamentalComponent: {1071 return;1072 }1073 case HostRoot:1074 case HostPortal: {1075 const portalOrRoot: {1076 containerInfo: Container,1077 pendingChildren: ChildSet,1078 ...1079 } = finishedWork.stateNode;1080 const {containerInfo, pendingChildren} = portalOrRoot;1081 replaceContainerChildren(containerInfo, pendingChildren);1082 return;1083 }1084 }1085 invariant(1086 false,1087 'This unit of work tag should not have side-effects. This error is ' +1088 'likely caused by a bug in React. Please file an issue.',1089 );1090}1091function getHostParentFiber(fiber: Fiber): Fiber {1092 let parent = fiber.return;1093 while (parent !== null) {1094 if (isHostParent(parent)) {1095 return parent;1096 }1097 parent = parent.return;1098 }1099 invariant(1100 false,1101 'Expected to find a host parent. This error is likely caused by a bug ' +1102 'in React. Please file an issue.',1103 );1104}1105function isHostParent(fiber: Fiber): boolean {1106 return (1107 fiber.tag === HostComponent ||1108 fiber.tag === HostRoot ||1109 fiber.tag === HostPortal1110 );1111}1112function getHostSibling(fiber: Fiber): ?Instance {1113 // We're going to search forward into the tree until we find a sibling host1114 // node. Unfortunately, if multiple insertions are done in a row we have to1115 // search past them. This leads to exponential search for the next sibling.1116 // TODO: Find a more efficient way to do this.1117 let node: Fiber = fiber;1118 siblings: while (true) {1119 // If we didn't find anything, let's try the next sibling.1120 while (node.sibling === null) {1121 if (node.return === null || isHostParent(node.return)) {1122 // If we pop out of the root or hit the parent the fiber we are the1123 // last sibling.1124 return null;1125 }1126 node = node.return;1127 }1128 node.sibling.return = node.return;1129 node = node.sibling;1130 while (1131 node.tag !== HostComponent &&1132 node.tag !== HostText &&1133 node.tag !== DehydratedFragment1134 ) {1135 // If it is not host node and, we might have a host node inside it.1136 // Try to search down until we find one.1137 if (node.flags & Placement) {1138 // If we don't have a child, try the siblings instead.1139 continue siblings;1140 }1141 // If we don't have a child, try the siblings instead.1142 // We also skip portals because they are not part of this host tree.1143 if (node.child === null || node.tag === HostPortal) {1144 continue siblings;1145 } else {1146 node.child.return = node;1147 node = node.child;1148 }1149 }1150 // Check if this host node is stable or about to be placed.1151 if (!(node.flags & Placement)) {1152 // Found it!1153 return node.stateNode;1154 }1155 }1156}1157function commitPlacement(finishedWork: Fiber): void {1158 if (!supportsMutation) {1159 return;1160 }1161 // Recursively insert all host nodes into the parent.1162 const parentFiber = getHostParentFiber(finishedWork);1163 // Note: these two variables *must* always be updated together.1164 let parent;1165 let isContainer;1166 const parentStateNode = parentFiber.stateNode;1167 switch (parentFiber.tag) {1168 case HostComponent:1169 parent = parentStateNode;1170 isContainer = false;1171 break;1172 case HostRoot:1173 parent = parentStateNode.containerInfo;1174 isContainer = true;1175 break;1176 case HostPortal:1177 parent = parentStateNode.containerInfo;1178 isContainer = true;1179 break;1180 case FundamentalComponent:1181 if (enableFundamentalAPI) {1182 parent = parentStateNode.instance;1183 isContainer = false;1184 }1185 // eslint-disable-next-line-no-fallthrough1186 default:1187 invariant(1188 false,1189 'Invalid host parent fiber. This error is likely caused by a bug ' +1190 'in React. Please file an issue.',1191 );1192 }1193 if (parentFiber.flags & ContentReset) {1194 // Reset the text content of the parent before doing any insertions1195 resetTextContent(parent);1196 // Clear ContentReset from the effect tag1197 parentFiber.flags &= ~ContentReset;1198 }1199 const before = getHostSibling(finishedWork);1200 // We only have the top Fiber that was inserted but we need to recurse down its1201 // children to find all the terminal nodes.1202 if (isContainer) {1203 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);1204 } else {1205 insertOrAppendPlacementNode(finishedWork, before, parent);1206 }1207}1208function insertOrAppendPlacementNodeIntoContainer(1209 node: Fiber,1210 before: ?Instance,1211 parent: Container,1212): void {1213 const {tag} = node;1214 const isHost = tag === HostComponent || tag === HostText;1215 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1216 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1217 if (before) {1218 insertInContainerBefore(parent, stateNode, before);1219 } else {1220 appendChildToContainer(parent, stateNode);1221 }1222 } else if (tag === HostPortal) {1223 // If the insertion itself is a portal, then we don't want to traverse1224 // down its children. Instead, we'll get insertions from each child in1225 // the portal directly.1226 } else {1227 const child = node.child;1228 if (child !== null) {1229 insertOrAppendPlacementNodeIntoContainer(child, before, parent);1230 let sibling = child.sibling;1231 while (sibling !== null) {1232 insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);1233 sibling = sibling.sibling;1234 }1235 }1236 }1237}1238function insertOrAppendPlacementNode(1239 node: Fiber,1240 before: ?Instance,1241 parent: Instance,1242): void {1243 const {tag} = node;1244 const isHost = tag === HostComponent || tag === HostText;1245 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1246 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1247 if (before) {1248 insertBefore(parent, stateNode, before);1249 } else {1250 appendChild(parent, stateNode);1251 }1252 } else if (tag === HostPortal) {1253 // If the insertion itself is a portal, then we don't want to traverse1254 // down its children. Instead, we'll get insertions from each child in1255 // the portal directly.1256 } else {1257 const child = node.child;1258 if (child !== null) {1259 insertOrAppendPlacementNode(child, before, parent);1260 let sibling = child.sibling;1261 while (sibling !== null) {1262 insertOrAppendPlacementNode(sibling, before, parent);1263 sibling = sibling.sibling;1264 }1265 }1266 }1267}1268function unmountHostComponents(1269 finishedRoot: FiberRoot,1270 current: Fiber,1271 renderPriorityLevel: ReactPriorityLevel,1272): void {1273 // We only have the top Fiber that was deleted but we need to recurse down its1274 // children to find all the terminal nodes.1275 let node: Fiber = current;1276 // Each iteration, currentParent is populated with node's host parent if not1277 // currentParentIsValid.1278 let currentParentIsValid = false;1279 // Note: these two variables *must* always be updated together.1280 let currentParent;1281 let currentParentIsContainer;1282 while (true) {1283 if (!currentParentIsValid) {1284 let parent = node.return;1285 findParent: while (true) {1286 invariant(1287 parent !== null,1288 'Expected to find a host parent. This error is likely caused by ' +1289 'a bug in React. Please file an issue.',1290 );1291 const parentStateNode = parent.stateNode;1292 switch (parent.tag) {1293 case HostComponent:1294 currentParent = parentStateNode;1295 currentParentIsContainer = false;1296 break findParent;1297 case HostRoot:1298 currentParent = parentStateNode.containerInfo;1299 currentParentIsContainer = true;1300 break findParent;1301 case HostPortal:1302 currentParent = parentStateNode.containerInfo;1303 currentParentIsContainer = true;1304 break findParent;1305 case FundamentalComponent:1306 if (enableFundamentalAPI) {1307 currentParent = parentStateNode.instance;1308 currentParentIsContainer = false;1309 }1310 }1311 parent = parent.return;1312 }1313 currentParentIsValid = true;1314 }1315 if (node.tag === HostComponent || node.tag === HostText) {1316 commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);1317 // After all the children have unmounted, it is now safe to remove the1318 // node from the tree.1319 if (currentParentIsContainer) {1320 removeChildFromContainer(1321 ((currentParent: any): Container),1322 (node.stateNode: Instance | TextInstance),1323 );1324 } else {1325 removeChild(1326 ((currentParent: any): Instance),1327 (node.stateNode: Instance | TextInstance),1328 );1329 }1330 // Don't visit children because we already visited them.1331 } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {1332 const fundamentalNode = node.stateNode.instance;1333 commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);1334 // After all the children have unmounted, it is now safe to remove the1335 // node from the tree.1336 if (currentParentIsContainer) {1337 removeChildFromContainer(1338 ((currentParent: any): Container),1339 (fundamentalNode: Instance),1340 );1341 } else {1342 removeChild(1343 ((currentParent: any): Instance),1344 (fundamentalNode: Instance),1345 );1346 }1347 } else if (1348 enableSuspenseServerRenderer &&1349 node.tag === DehydratedFragment1350 ) {1351 if (enableSuspenseCallback) {1352 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1353 if (hydrationCallbacks !== null) {1354 const onDeleted = hydrationCallbacks.onDeleted;1355 if (onDeleted) {1356 onDeleted((node.stateNode: SuspenseInstance));1357 }1358 }1359 }1360 // Delete the dehydrated suspense boundary and all of its content.1361 if (currentParentIsContainer) {1362 clearSuspenseBoundaryFromContainer(1363 ((currentParent: any): Container),1364 (node.stateNode: SuspenseInstance),1365 );1366 } else {1367 clearSuspenseBoundary(1368 ((currentParent: any): Instance),1369 (node.stateNode: SuspenseInstance),1370 );1371 }1372 } else if (node.tag === HostPortal) {1373 if (node.child !== null) {1374 // When we go into a portal, it becomes the parent to remove from.1375 // We will reassign it back when we pop the portal on the way up.1376 currentParent = node.stateNode.containerInfo;1377 currentParentIsContainer = true;1378 // Visit children because portals might contain host components.1379 node.child.return = node;1380 node = node.child;1381 continue;1382 }1383 } else {1384 commitUnmount(finishedRoot, node, renderPriorityLevel);1385 // Visit children because we may find more host components below.1386 if (node.child !== null) {1387 node.child.return = node;1388 node = node.child;1389 continue;1390 }1391 }1392 if (node === current) {1393 return;1394 }1395 while (node.sibling === null) {1396 if (node.return === null || node.return === current) {1397 return;1398 }1399 node = node.return;1400 if (node.tag === HostPortal) {1401 // When we go out of the portal, we need to restore the parent.1402 // Since we don't keep a stack of them, we will search for it.1403 currentParentIsValid = false;1404 }1405 }1406 node.sibling.return = node.return;1407 node = node.sibling;1408 }1409}1410function commitDeletion(1411 finishedRoot: FiberRoot,1412 current: Fiber,1413 renderPriorityLevel: ReactPriorityLevel,1414): void {1415 if (supportsMutation) {1416 // Recursively delete all host nodes from the parent.1417 // Detach refs and call componentWillUnmount() on the whole subtree.1418 unmountHostComponents(finishedRoot, current, renderPriorityLevel);1419 } else {1420 // Detach refs and call componentWillUnmount() on the whole subtree.1421 commitNestedUnmounts(finishedRoot, current, renderPriorityLevel);1422 }1423 const alternate = current.alternate;1424 detachFiberMutation(current);1425 if (alternate !== null) {1426 detachFiberMutation(alternate);1427 }1428}1429function commitWork(current: Fiber | null, finishedWork: Fiber): void {1430 if (!supportsMutation) {1431 switch (finishedWork.tag) {1432 case FunctionComponent:1433 case ForwardRef:1434 case MemoComponent:1435 case SimpleMemoComponent: {1436 // Layout effects are destroyed during the mutation phase so that all1437 // destroy functions for all fibers are called before any create functions.1438 // This prevents sibling component effects from interfering with each other,1439 // e.g. a destroy function in one component should never override a ref set1440 // by a create function in another component during the same commit.1441 if (1442 enableProfilerTimer &&1443 enableProfilerCommitHooks &&1444 finishedWork.mode & ProfileMode1445 ) {1446 try {1447 startLayoutEffectTimer();1448 commitHookEffectListUnmount(1449 HookLayout | HookHasEffect,1450 finishedWork,1451 );1452 } finally {1453 recordLayoutEffectDuration(finishedWork);1454 }1455 } else {1456 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1457 }1458 return;1459 }1460 case Profiler: {1461 return;1462 }1463 case SuspenseComponent: {1464 commitSuspenseComponent(finishedWork);1465 attachSuspenseRetryListeners(finishedWork);1466 return;1467 }1468 case SuspenseListComponent: {1469 attachSuspenseRetryListeners(finishedWork);1470 return;1471 }1472 case HostRoot: {1473 if (supportsHydration) {1474 const root: FiberRoot = finishedWork.stateNode;1475 if (root.hydrate) {1476 // We've just hydrated. No need to hydrate again.1477 root.hydrate = false;1478 commitHydratedContainer(root.containerInfo);1479 }1480 }1481 break;1482 }1483 case OffscreenComponent:1484 case LegacyHiddenComponent: {1485 return;1486 }1487 }1488 commitContainer(finishedWork);1489 return;1490 }1491 switch (finishedWork.tag) {1492 case FunctionComponent:1493 case ForwardRef:1494 case MemoComponent:1495 case SimpleMemoComponent: {1496 // Layout effects are destroyed during the mutation phase so that all1497 // destroy functions for all fibers are called before any create functions.1498 // This prevents sibling component effects from interfering with each other,1499 // e.g. a destroy function in one component should never override a ref set1500 // by a create function in another component during the same commit.1501 if (1502 enableProfilerTimer &&1503 enableProfilerCommitHooks &&1504 finishedWork.mode & ProfileMode1505 ) {1506 try {1507 startLayoutEffectTimer();1508 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1509 } finally {1510 recordLayoutEffectDuration(finishedWork);1511 }1512 } else {1513 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1514 }1515 return;1516 }1517 case ClassComponent: {1518 return;1519 }1520 case HostComponent: {1521 const instance: Instance = finishedWork.stateNode;1522 if (instance != null) {1523 // Commit the work prepared earlier.1524 const newProps = finishedWork.memoizedProps;1525 // For hydration we reuse the update path but we treat the oldProps1526 // as the newProps. The updatePayload will contain the real change in1527 // this case.1528 const oldProps = current !== null ? current.memoizedProps : newProps;1529 const type = finishedWork.type;1530 // TODO: Type the updateQueue to be specific to host components.1531 const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);1532 finishedWork.updateQueue = null;1533 if (updatePayload !== null) {1534 commitUpdate(1535 instance,1536 updatePayload,1537 type,1538 oldProps,1539 newProps,1540 finishedWork,1541 );1542 }1543 }1544 return;1545 }1546 case HostText: {1547 invariant(1548 finishedWork.stateNode !== null,1549 'This should have a text node initialized. This error is likely ' +1550 'caused by a bug in React. Please file an issue.',1551 );1552 const textInstance: TextInstance = finishedWork.stateNode;1553 const newText: string = finishedWork.memoizedProps;1554 // For hydration we reuse the update path but we treat the oldProps1555 // as the newProps. The updatePayload will contain the real change in1556 // this case.1557 const oldText: string =1558 current !== null ? current.memoizedProps : newText;1559 commitTextUpdate(textInstance, oldText, newText);1560 return;1561 }1562 case HostRoot: {1563 if (supportsHydration) {1564 const root: FiberRoot = finishedWork.stateNode;1565 if (root.hydrate) {1566 // We've just hydrated. No need to hydrate again.1567 root.hydrate = false;1568 commitHydratedContainer(root.containerInfo);1569 }1570 }1571 return;1572 }1573 case Profiler: {1574 return;1575 }1576 case SuspenseComponent: {1577 commitSuspenseComponent(finishedWork);1578 attachSuspenseRetryListeners(finishedWork);1579 return;1580 }1581 case SuspenseListComponent: {1582 attachSuspenseRetryListeners(finishedWork);1583 return;1584 }1585 case IncompleteClassComponent: {1586 return;1587 }1588 case FundamentalComponent: {1589 if (enableFundamentalAPI) {1590 const fundamentalInstance = finishedWork.stateNode;1591 updateFundamentalComponent(fundamentalInstance);1592 return;1593 }1594 break;1595 }1596 case ScopeComponent: {1597 if (enableScopeAPI) {1598 const scopeInstance = finishedWork.stateNode;1599 prepareScopeUpdate(scopeInstance, finishedWork);1600 return;1601 }1602 break;1603 }1604 case OffscreenComponent:1605 case LegacyHiddenComponent: {1606 const newState: OffscreenState | null = finishedWork.memoizedState;1607 const isHidden = newState !== null;1608 hideOrUnhideAllChildren(finishedWork, isHidden);1609 return;1610 }1611 }1612 invariant(1613 false,1614 'This unit of work tag should not have side-effects. This error is ' +1615 'likely caused by a bug in React. Please file an issue.',1616 );1617}1618function commitSuspenseComponent(finishedWork: Fiber) {1619 const newState: SuspenseState | null = finishedWork.memoizedState;1620 if (newState !== null) {1621 markCommitTimeOfFallback();1622 if (supportsMutation) {1623 // Hide the Offscreen component that contains the primary children. TODO:1624 // Ideally, this effect would have been scheduled on the Offscreen fiber1625 // itself. That's how unhiding works: the Offscreen component schedules an1626 // effect on itself. However, in this case, the component didn't complete,1627 // so the fiber was never added to the effect list in the normal path. We1628 // could have appended it to the effect list in the Suspense component's1629 // second pass, but doing it this way is less complicated. This would be1630 // simpler if we got rid of the effect list and traversed the tree, like1631 // we're planning to do.1632 const primaryChildParent: Fiber = (finishedWork.child: any);1633 hideOrUnhideAllChildren(primaryChildParent, true);1634 }1635 }1636 if (enableSuspenseCallback && newState !== null) {1637 const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;1638 if (typeof suspenseCallback === 'function') {1639 const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);1640 if (wakeables !== null) {1641 suspenseCallback(new Set(wakeables));1642 }1643 } else if (__DEV__) {1644 if (suspenseCallback !== undefined) {1645 console.error('Unexpected type for suspenseCallback.');1646 }1647 }1648 }1649}1650function commitSuspenseHydrationCallbacks(1651 finishedRoot: FiberRoot,1652 finishedWork: Fiber,1653) {1654 if (!supportsHydration) {1655 return;1656 }1657 const newState: SuspenseState | null = finishedWork.memoizedState;1658 if (newState === null) {1659 const current = finishedWork.alternate;1660 if (current !== null) {1661 const prevState: SuspenseState | null = current.memoizedState;1662 if (prevState !== null) {1663 const suspenseInstance = prevState.dehydrated;1664 if (suspenseInstance !== null) {1665 commitHydratedSuspenseInstance(suspenseInstance);1666 if (enableSuspenseCallback) {1667 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1668 if (hydrationCallbacks !== null) {1669 const onHydrated = hydrationCallbacks.onHydrated;1670 if (onHydrated) {1671 onHydrated(suspenseInstance);1672 }1673 }1674 }1675 }1676 }1677 }1678 }1679}1680function attachSuspenseRetryListeners(finishedWork: Fiber) {1681 // If this boundary just timed out, then it will have a set of wakeables.1682 // For each wakeable, attach a listener so that when it resolves, React1683 // attempts to re-render the boundary in the primary (pre-timeout) state.1684 const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);1685 if (wakeables !== null) {1686 finishedWork.updateQueue = null;1687 let retryCache = finishedWork.stateNode;1688 if (retryCache === null) {1689 retryCache = finishedWork.stateNode = new PossiblyWeakSet();1690 }1691 wakeables.forEach(wakeable => {1692 // Memoize using the boundary fiber to prevent redundant listeners.1693 let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);1694 if (!retryCache.has(wakeable)) {1695 if (enableSchedulerTracing) {1696 if (wakeable.__reactDoNotTraceInteractions !== true) {1697 retry = Schedule_tracing_wrap(retry);1698 }1699 }1700 retryCache.add(wakeable);1701 wakeable.then(retry, retry);1702 }1703 });1704 }1705}1706// This function detects when a Suspense boundary goes from visible to hidden.1707// It returns false if the boundary is already hidden.1708// TODO: Use an effect tag.1709export function isSuspenseBoundaryBeingHidden(1710 current: Fiber | null,1711 finishedWork: Fiber,1712): boolean {1713 if (current !== null) {1714 const oldState: SuspenseState | null = current.memoizedState;1715 if (oldState === null || oldState.dehydrated !== null) {1716 const newState: SuspenseState | null = finishedWork.memoizedState;1717 return newState !== null && newState.dehydrated === null;1718 }1719 }1720 return false;1721}1722function commitResetTextContent(current: Fiber) {1723 if (!supportsMutation) {1724 return;1725 }1726 resetTextContent(current.stateNode);1727}1728export {1729 commitBeforeMutationLifeCycles,1730 commitResetTextContent,1731 commitPlacement,1732 commitDeletion,1733 commitWork,1734 commitLifeCycles,1735 commitAttachRef,1736 commitDetachRef,...
ReactFiberCommitWork.old.js
Source:ReactFiberCommitWork.old.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * 8 */9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';27import {28 enableSchedulerTracing,29 enableProfilerTimer,30 enableProfilerCommitHooks,31 enableSuspenseServerRenderer,32 enableFundamentalAPI,33 enableSuspenseCallback,34 enableScopeAPI,35} from 'shared/ReactFeatureFlags';36import {37 FunctionComponent,38 ForwardRef,39 ClassComponent,40 HostRoot,41 HostComponent,42 HostText,43 HostPortal,44 Profiler,45 SuspenseComponent,46 DehydratedFragment,47 IncompleteClassComponent,48 MemoComponent,49 SimpleMemoComponent,50 SuspenseListComponent,51 FundamentalComponent,52 ScopeComponent,53 Block,54 OffscreenComponent,55 LegacyHiddenComponent,56} from './ReactWorkTags';57import {58 invokeGuardedCallback,59 hasCaughtError,60 clearCaughtError,61} from 'shared/ReactErrorUtils';62import {63 NoFlags,64 ContentReset,65 Placement,66 Snapshot,67 Update,68} from './ReactFiberFlags';69import getComponentName from 'shared/getComponentName';70import invariant from 'shared/invariant';71import {onCommitUnmount} from './ReactFiberDevToolsHook.old';72import {resolveDefaultProps} from './ReactFiberLazyComponent.old';73import {74 getCommitTime,75 recordLayoutEffectDuration,76 startLayoutEffectTimer,77} from './ReactProfilerTimer.old';78import {ProfileMode} from './ReactTypeOfMode';79import {commitUpdateQueue} from './ReactUpdateQueue.old';80import {81 getPublicInstance,82 supportsMutation,83 supportsPersistence,84 supportsHydration,85 commitMount,86 commitUpdate,87 resetTextContent,88 commitTextUpdate,89 appendChild,90 appendChildToContainer,91 insertBefore,92 insertInContainerBefore,93 removeChild,94 removeChildFromContainer,95 clearSuspenseBoundary,96 clearSuspenseBoundaryFromContainer,97 replaceContainerChildren,98 createContainerChildSet,99 hideInstance,100 hideTextInstance,101 unhideInstance,102 unhideTextInstance,103 unmountFundamentalComponent,104 updateFundamentalComponent,105 commitHydratedContainer,106 commitHydratedSuspenseInstance,107 clearContainer,108 prepareScopeUpdate,109} from './ReactFiberHostConfig';110import {111 captureCommitPhaseError,112 resolveRetryWakeable,113 markCommitTimeOfFallback,114 enqueuePendingPassiveHookEffectMount,115 enqueuePendingPassiveHookEffectUnmount,116 enqueuePendingPassiveProfilerEffect,117} from './ReactFiberWorkLoop.old';118import {119 NoFlags as NoHookEffect,120 HasEffect as HookHasEffect,121 Layout as HookLayout,122 Passive as HookPassive,123} from './ReactHookEffectTags';124import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.old';125let didWarnAboutUndefinedSnapshotBeforeUpdate = null;126if (__DEV__) {127 didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();128}129const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;130const callComponentWillUnmountWithTimer = function(current, instance) {131 instance.props = current.memoizedProps;132 instance.state = current.memoizedState;133 if (134 enableProfilerTimer &&135 enableProfilerCommitHooks &&136 current.mode & ProfileMode137 ) {138 try {139 startLayoutEffectTimer();140 instance.componentWillUnmount();141 } finally {142 recordLayoutEffectDuration(current);143 }144 } else {145 instance.componentWillUnmount();146 }147};148// Capture errors so they don't interrupt unmounting.149function safelyCallComponentWillUnmount(current , instance ) {150 if (__DEV__) {151 invokeGuardedCallback(152 null,153 callComponentWillUnmountWithTimer,154 null,155 current,156 instance,157 );158 if (hasCaughtError()) {159 const unmountError = clearCaughtError();160 captureCommitPhaseError(current, unmountError);161 }162 } else {163 try {164 callComponentWillUnmountWithTimer(current, instance);165 } catch (unmountError) {166 captureCommitPhaseError(current, unmountError);167 }168 }169}170function safelyDetachRef(current ) {171 const ref = current.ref;172 if (ref !== null) {173 if (typeof ref === 'function') {174 if (__DEV__) {175 invokeGuardedCallback(null, ref, null, null);176 if (hasCaughtError()) {177 const refError = clearCaughtError();178 captureCommitPhaseError(current, refError);179 }180 } else {181 try {182 ref(null);183 } catch (refError) {184 captureCommitPhaseError(current, refError);185 }186 }187 } else {188 ref.current = null;189 }190 }191}192function safelyCallDestroy(current , destroy ) {193 if (__DEV__) {194 invokeGuardedCallback(null, destroy, null);195 if (hasCaughtError()) {196 const error = clearCaughtError();197 captureCommitPhaseError(current, error);198 }199 } else {200 try {201 destroy();202 } catch (error) {203 captureCommitPhaseError(current, error);204 }205 }206}207function commitBeforeMutationLifeCycles(208 current ,209 finishedWork ,210) {211 switch (finishedWork.tag) {212 case FunctionComponent:213 case ForwardRef:214 case SimpleMemoComponent:215 case Block: {216 return;217 }218 case ClassComponent: {219 if (finishedWork.flags & Snapshot) {220 if (current !== null) {221 const prevProps = current.memoizedProps;222 const prevState = current.memoizedState;223 const instance = finishedWork.stateNode;224 // We could update instance props and state here,225 // but instead we rely on them being set during last render.226 // TODO: revisit this when we implement resuming.227 if (__DEV__) {228 if (229 finishedWork.type === finishedWork.elementType &&230 !didWarnAboutReassigningProps231 ) {232 if (instance.props !== finishedWork.memoizedProps) {233 console.error(234 'Expected %s props to match memoized props before ' +235 'getSnapshotBeforeUpdate. ' +236 'This might either be because of a bug in React, or because ' +237 'a component reassigns its own `this.props`. ' +238 'Please file an issue.',239 getComponentName(finishedWork.type) || 'instance',240 );241 }242 if (instance.state !== finishedWork.memoizedState) {243 console.error(244 'Expected %s state to match memoized state before ' +245 'getSnapshotBeforeUpdate. ' +246 'This might either be because of a bug in React, or because ' +247 'a component reassigns its own `this.state`. ' +248 'Please file an issue.',249 getComponentName(finishedWork.type) || 'instance',250 );251 }252 }253 }254 const snapshot = instance.getSnapshotBeforeUpdate(255 finishedWork.elementType === finishedWork.type256 ? prevProps257 : resolveDefaultProps(finishedWork.type, prevProps),258 prevState,259 );260 if (__DEV__) {261 const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate ) );262 if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {263 didWarnSet.add(finishedWork.type);264 console.error(265 '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +266 'must be returned. You have returned undefined.',267 getComponentName(finishedWork.type),268 );269 }270 }271 instance.__reactInternalSnapshotBeforeUpdate = snapshot;272 }273 }274 return;275 }276 case HostRoot: {277 if (supportsMutation) {278 if (finishedWork.flags & Snapshot) {279 const root = finishedWork.stateNode;280 clearContainer(root.containerInfo);281 }282 }283 return;284 }285 case HostComponent:286 case HostText:287 case HostPortal:288 case IncompleteClassComponent:289 // Nothing to do for these component types290 return;291 }292 invariant(293 false,294 'This unit of work tag should not have side-effects. This error is ' +295 'likely caused by a bug in React. Please file an issue.',296 );297}298function commitHookEffectListUnmount(tag , finishedWork ) {299 const updateQueue = (finishedWork.updateQueue );300 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;301 if (lastEffect !== null) {302 const firstEffect = lastEffect.next;303 let effect = firstEffect;304 do {305 if ((effect.tag & tag) === tag) {306 // Unmount307 const destroy = effect.destroy;308 effect.destroy = undefined;309 if (destroy !== undefined) {310 destroy();311 }312 }313 effect = effect.next;314 } while (effect !== firstEffect);315 }316}317function commitHookEffectListMount(tag , finishedWork ) {318 const updateQueue = (finishedWork.updateQueue );319 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;320 if (lastEffect !== null) {321 const firstEffect = lastEffect.next;322 let effect = firstEffect;323 do {324 if ((effect.tag & tag) === tag) {325 // Mount326 const create = effect.create;327 effect.destroy = create();328 if (__DEV__) {329 const destroy = effect.destroy;330 if (destroy !== undefined && typeof destroy !== 'function') {331 let addendum;332 if (destroy === null) {333 addendum =334 ' You returned null. If your effect does not require clean ' +335 'up, return undefined (or nothing).';336 } else if (typeof destroy.then === 'function') {337 addendum =338 '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +339 'Instead, write the async function inside your effect ' +340 'and call it immediately:\n\n' +341 'useEffect(() => {\n' +342 ' async function fetchData() {\n' +343 ' // You can await here\n' +344 ' const response = await MyAPI.getData(someId);\n' +345 ' // ...\n' +346 ' }\n' +347 ' fetchData();\n' +348 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +349 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';350 } else {351 addendum = ' You returned: ' + destroy;352 }353 console.error(354 'An effect function must not return anything besides a function, ' +355 'which is used for clean-up.%s',356 addendum,357 );358 }359 }360 }361 effect = effect.next;362 } while (effect !== firstEffect);363 }364}365function schedulePassiveEffects(finishedWork ) {366 const updateQueue = (finishedWork.updateQueue );367 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;368 if (lastEffect !== null) {369 const firstEffect = lastEffect.next;370 let effect = firstEffect;371 do {372 const {next, tag} = effect;373 if (374 (tag & HookPassive) !== NoHookEffect &&375 (tag & HookHasEffect) !== NoHookEffect376 ) {377 enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);378 enqueuePendingPassiveHookEffectMount(finishedWork, effect);379 }380 effect = next;381 } while (effect !== firstEffect);382 }383}384export function commitPassiveEffectDurations(385 finishedRoot ,386 finishedWork ,387) {388 if (enableProfilerTimer && enableProfilerCommitHooks) {389 // Only Profilers with work in their subtree will have an Update effect scheduled.390 if ((finishedWork.flags & Update) !== NoFlags) {391 switch (finishedWork.tag) {392 case Profiler: {393 const {passiveEffectDuration} = finishedWork.stateNode;394 const {id, onPostCommit} = finishedWork.memoizedProps;395 // This value will still reflect the previous commit phase.396 // It does not get reset until the start of the next commit phase.397 const commitTime = getCommitTime();398 if (typeof onPostCommit === 'function') {399 if (enableSchedulerTracing) {400 onPostCommit(401 id,402 finishedWork.alternate === null ? 'mount' : 'update',403 passiveEffectDuration,404 commitTime,405 finishedRoot.memoizedInteractions,406 );407 } else {408 onPostCommit(409 id,410 finishedWork.alternate === null ? 'mount' : 'update',411 passiveEffectDuration,412 commitTime,413 );414 }415 }416 // Bubble times to the next nearest ancestor Profiler.417 // After we process that Profiler, we'll bubble further up.418 let parentFiber = finishedWork.return;419 while (parentFiber !== null) {420 if (parentFiber.tag === Profiler) {421 const parentStateNode = parentFiber.stateNode;422 parentStateNode.passiveEffectDuration += passiveEffectDuration;423 break;424 }425 parentFiber = parentFiber.return;426 }427 break;428 }429 default:430 break;431 }432 }433 }434}435function commitLifeCycles(436 finishedRoot ,437 current ,438 finishedWork ,439 committedLanes ,440) {441 switch (finishedWork.tag) {442 case FunctionComponent:443 case ForwardRef:444 case SimpleMemoComponent:445 case Block: {446 // At this point layout effects have already been destroyed (during mutation phase).447 // This is done to prevent sibling component effects from interfering with each other,448 // e.g. a destroy function in one component should never override a ref set449 // by a create function in another component during the same commit.450 if (451 enableProfilerTimer &&452 enableProfilerCommitHooks &&453 finishedWork.mode & ProfileMode454 ) {455 try {456 startLayoutEffectTimer();457 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);458 } finally {459 recordLayoutEffectDuration(finishedWork);460 }461 } else {462 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);463 }464 schedulePassiveEffects(finishedWork);465 return;466 }467 case ClassComponent: {468 const instance = finishedWork.stateNode;469 if (finishedWork.flags & Update) {470 if (current === null) {471 // We could update instance props and state here,472 // but instead we rely on them being set during last render.473 // TODO: revisit this when we implement resuming.474 if (__DEV__) {475 if (476 finishedWork.type === finishedWork.elementType &&477 !didWarnAboutReassigningProps478 ) {479 if (instance.props !== finishedWork.memoizedProps) {480 console.error(481 'Expected %s props to match memoized props before ' +482 'componentDidMount. ' +483 'This might either be because of a bug in React, or because ' +484 'a component reassigns its own `this.props`. ' +485 'Please file an issue.',486 getComponentName(finishedWork.type) || 'instance',487 );488 }489 if (instance.state !== finishedWork.memoizedState) {490 console.error(491 'Expected %s state to match memoized state before ' +492 'componentDidMount. ' +493 'This might either be because of a bug in React, or because ' +494 'a component reassigns its own `this.state`. ' +495 'Please file an issue.',496 getComponentName(finishedWork.type) || 'instance',497 );498 }499 }500 }501 if (502 enableProfilerTimer &&503 enableProfilerCommitHooks &&504 finishedWork.mode & ProfileMode505 ) {506 try {507 startLayoutEffectTimer();508 instance.componentDidMount();509 } finally {510 recordLayoutEffectDuration(finishedWork);511 }512 } else {513 instance.componentDidMount();514 }515 } else {516 const prevProps =517 finishedWork.elementType === finishedWork.type518 ? current.memoizedProps519 : resolveDefaultProps(finishedWork.type, current.memoizedProps);520 const prevState = current.memoizedState;521 // We could update instance props and state here,522 // but instead we rely on them being set during last render.523 // TODO: revisit this when we implement resuming.524 if (__DEV__) {525 if (526 finishedWork.type === finishedWork.elementType &&527 !didWarnAboutReassigningProps528 ) {529 if (instance.props !== finishedWork.memoizedProps) {530 console.error(531 'Expected %s props to match memoized props before ' +532 'componentDidUpdate. ' +533 'This might either be because of a bug in React, or because ' +534 'a component reassigns its own `this.props`. ' +535 'Please file an issue.',536 getComponentName(finishedWork.type) || 'instance',537 );538 }539 if (instance.state !== finishedWork.memoizedState) {540 console.error(541 'Expected %s state to match memoized state before ' +542 'componentDidUpdate. ' +543 'This might either be because of a bug in React, or because ' +544 'a component reassigns its own `this.state`. ' +545 'Please file an issue.',546 getComponentName(finishedWork.type) || 'instance',547 );548 }549 }550 }551 if (552 enableProfilerTimer &&553 enableProfilerCommitHooks &&554 finishedWork.mode & ProfileMode555 ) {556 try {557 startLayoutEffectTimer();558 instance.componentDidUpdate(559 prevProps,560 prevState,561 instance.__reactInternalSnapshotBeforeUpdate,562 );563 } finally {564 recordLayoutEffectDuration(finishedWork);565 }566 } else {567 instance.componentDidUpdate(568 prevProps,569 prevState,570 instance.__reactInternalSnapshotBeforeUpdate,571 );572 }573 }574 }575 // TODO: I think this is now always non-null by the time it reaches the576 // commit phase. Consider removing the type check.577 const updateQueue 578 579 = (finishedWork.updateQueue );580 if (updateQueue !== null) {581 if (__DEV__) {582 if (583 finishedWork.type === finishedWork.elementType &&584 !didWarnAboutReassigningProps585 ) {586 if (instance.props !== finishedWork.memoizedProps) {587 console.error(588 'Expected %s props to match memoized props before ' +589 'processing the update queue. ' +590 'This might either be because of a bug in React, or because ' +591 'a component reassigns its own `this.props`. ' +592 'Please file an issue.',593 getComponentName(finishedWork.type) || 'instance',594 );595 }596 if (instance.state !== finishedWork.memoizedState) {597 console.error(598 'Expected %s state to match memoized state before ' +599 'processing the update queue. ' +600 'This might either be because of a bug in React, or because ' +601 'a component reassigns its own `this.state`. ' +602 'Please file an issue.',603 getComponentName(finishedWork.type) || 'instance',604 );605 }606 }607 }608 // We could update instance props and state here,609 // but instead we rely on them being set during last render.610 // TODO: revisit this when we implement resuming.611 commitUpdateQueue(finishedWork, updateQueue, instance);612 }613 return;614 }615 case HostRoot: {616 // TODO: I think this is now always non-null by the time it reaches the617 // commit phase. Consider removing the type check.618 const updateQueue 619 620 = (finishedWork.updateQueue );621 if (updateQueue !== null) {622 let instance = null;623 if (finishedWork.child !== null) {624 switch (finishedWork.child.tag) {625 case HostComponent:626 instance = getPublicInstance(finishedWork.child.stateNode);627 break;628 case ClassComponent:629 instance = finishedWork.child.stateNode;630 break;631 }632 }633 commitUpdateQueue(finishedWork, updateQueue, instance);634 }635 return;636 }637 case HostComponent: {638 const instance = finishedWork.stateNode;639 // Renderers may schedule work to be done after host components are mounted640 // (eg DOM renderer may schedule auto-focus for inputs and form controls).641 // These effects should only be committed when components are first mounted,642 // aka when there is no current/alternate.643 if (current === null && finishedWork.flags & Update) {644 const type = finishedWork.type;645 const props = finishedWork.memoizedProps;646 commitMount(instance, type, props, finishedWork);647 }648 return;649 }650 case HostText: {651 // We have no life-cycles associated with text.652 return;653 }654 case HostPortal: {655 // We have no life-cycles associated with portals.656 return;657 }658 case Profiler: {659 if (enableProfilerTimer) {660 const {onCommit, onRender} = finishedWork.memoizedProps;661 const {effectDuration} = finishedWork.stateNode;662 const commitTime = getCommitTime();663 if (typeof onRender === 'function') {664 if (enableSchedulerTracing) {665 onRender(666 finishedWork.memoizedProps.id,667 current === null ? 'mount' : 'update',668 finishedWork.actualDuration,669 finishedWork.treeBaseDuration,670 finishedWork.actualStartTime,671 commitTime,672 finishedRoot.memoizedInteractions,673 );674 } else {675 onRender(676 finishedWork.memoizedProps.id,677 current === null ? 'mount' : 'update',678 finishedWork.actualDuration,679 finishedWork.treeBaseDuration,680 finishedWork.actualStartTime,681 commitTime,682 );683 }684 }685 if (enableProfilerCommitHooks) {686 if (typeof onCommit === 'function') {687 if (enableSchedulerTracing) {688 onCommit(689 finishedWork.memoizedProps.id,690 current === null ? 'mount' : 'update',691 effectDuration,692 commitTime,693 finishedRoot.memoizedInteractions,694 );695 } else {696 onCommit(697 finishedWork.memoizedProps.id,698 current === null ? 'mount' : 'update',699 effectDuration,700 commitTime,701 );702 }703 }704 // Schedule a passive effect for this Profiler to call onPostCommit hooks.705 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,706 // because the effect is also where times bubble to parent Profilers.707 enqueuePendingPassiveProfilerEffect(finishedWork);708 // Propagate layout effect durations to the next nearest Profiler ancestor.709 // Do not reset these values until the next render so DevTools has a chance to read them first.710 let parentFiber = finishedWork.return;711 while (parentFiber !== null) {712 if (parentFiber.tag === Profiler) {713 const parentStateNode = parentFiber.stateNode;714 parentStateNode.effectDuration += effectDuration;715 break;716 }717 parentFiber = parentFiber.return;718 }719 }720 }721 return;722 }723 case SuspenseComponent: {724 commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);725 return;726 }727 case SuspenseListComponent:728 case IncompleteClassComponent:729 case FundamentalComponent:730 case ScopeComponent:731 case OffscreenComponent:732 case LegacyHiddenComponent:733 return;734 }735 invariant(736 false,737 'This unit of work tag should not have side-effects. This error is ' +738 'likely caused by a bug in React. Please file an issue.',739 );740}741function hideOrUnhideAllChildren(finishedWork, isHidden) {742 if (supportsMutation) {743 // We only have the top Fiber that was inserted but we need to recurse down its744 // children to find all the terminal nodes.745 let node = finishedWork;746 while (true) {747 if (node.tag === HostComponent) {748 const instance = node.stateNode;749 if (isHidden) {750 hideInstance(instance);751 } else {752 unhideInstance(node.stateNode, node.memoizedProps);753 }754 } else if (node.tag === HostText) {755 const instance = node.stateNode;756 if (isHidden) {757 hideTextInstance(instance);758 } else {759 unhideTextInstance(instance, node.memoizedProps);760 }761 } else if (762 (node.tag === OffscreenComponent ||763 node.tag === LegacyHiddenComponent) &&764 (node.memoizedState ) !== null &&765 node !== finishedWork766 ) {767 // Found a nested Offscreen component that is hidden. Don't search768 // any deeper. This tree should remain hidden.769 } else if (node.child !== null) {770 node.child.return = node;771 node = node.child;772 continue;773 }774 if (node === finishedWork) {775 return;776 }777 while (node.sibling === null) {778 if (node.return === null || node.return === finishedWork) {779 return;780 }781 node = node.return;782 }783 node.sibling.return = node.return;784 node = node.sibling;785 }786 }787}788function commitAttachRef(finishedWork ) {789 const ref = finishedWork.ref;790 if (ref !== null) {791 const instance = finishedWork.stateNode;792 let instanceToUse;793 switch (finishedWork.tag) {794 case HostComponent:795 instanceToUse = getPublicInstance(instance);796 break;797 default:798 instanceToUse = instance;799 }800 // Moved outside to ensure DCE works with this flag801 if (enableScopeAPI && finishedWork.tag === ScopeComponent) {802 instanceToUse = instance;803 }804 if (typeof ref === 'function') {805 ref(instanceToUse);806 } else {807 if (__DEV__) {808 if (!ref.hasOwnProperty('current')) {809 console.error(810 'Unexpected ref object provided for %s. ' +811 'Use either a ref-setter function or React.createRef().',812 getComponentName(finishedWork.type),813 );814 }815 }816 ref.current = instanceToUse;817 }818 }819}820function commitDetachRef(current ) {821 const currentRef = current.ref;822 if (currentRef !== null) {823 if (typeof currentRef === 'function') {824 currentRef(null);825 } else {826 currentRef.current = null;827 }828 }829}830// User-originating errors (lifecycles and refs) should not interrupt831// deletion, so don't let them throw. Host-originating errors should832// interrupt deletion, so it's okay833function commitUnmount(834 finishedRoot ,835 current ,836 renderPriorityLevel ,837) {838 onCommitUnmount(current);839 switch (current.tag) {840 case FunctionComponent:841 case ForwardRef:842 case MemoComponent:843 case SimpleMemoComponent:844 case Block: {845 const updateQueue = (current.updateQueue );846 if (updateQueue !== null) {847 const lastEffect = updateQueue.lastEffect;848 if (lastEffect !== null) {849 const firstEffect = lastEffect.next;850 let effect = firstEffect;851 do {852 const {destroy, tag} = effect;853 if (destroy !== undefined) {854 if ((tag & HookPassive) !== NoHookEffect) {855 enqueuePendingPassiveHookEffectUnmount(current, effect);856 } else {857 if (858 enableProfilerTimer &&859 enableProfilerCommitHooks &&860 current.mode & ProfileMode861 ) {862 startLayoutEffectTimer();863 safelyCallDestroy(current, destroy);864 recordLayoutEffectDuration(current);865 } else {866 safelyCallDestroy(current, destroy);867 }868 }869 }870 effect = effect.next;871 } while (effect !== firstEffect);872 }873 }874 return;875 }876 case ClassComponent: {877 safelyDetachRef(current);878 const instance = current.stateNode;879 if (typeof instance.componentWillUnmount === 'function') {880 safelyCallComponentWillUnmount(current, instance);881 }882 return;883 }884 case HostComponent: {885 safelyDetachRef(current);886 return;887 }888 case HostPortal: {889 // TODO: this is recursive.890 // We are also not using this parent because891 // the portal will get pushed immediately.892 if (supportsMutation) {893 unmountHostComponents(finishedRoot, current, renderPriorityLevel);894 } else if (supportsPersistence) {895 emptyPortalContainer(current);896 }897 return;898 }899 case FundamentalComponent: {900 if (enableFundamentalAPI) {901 const fundamentalInstance = current.stateNode;902 if (fundamentalInstance !== null) {903 unmountFundamentalComponent(fundamentalInstance);904 current.stateNode = null;905 }906 }907 return;908 }909 case DehydratedFragment: {910 if (enableSuspenseCallback) {911 const hydrationCallbacks = finishedRoot.hydrationCallbacks;912 if (hydrationCallbacks !== null) {913 const onDeleted = hydrationCallbacks.onDeleted;914 if (onDeleted) {915 onDeleted((current.stateNode ));916 }917 }918 }919 return;920 }921 case ScopeComponent: {922 if (enableScopeAPI) {923 safelyDetachRef(current);924 }925 return;926 }927 }928}929function commitNestedUnmounts(930 finishedRoot ,931 root ,932 renderPriorityLevel ,933) {934 // While we're inside a removed host node we don't want to call935 // removeChild on the inner nodes because they're removed by the top936 // call anyway. We also want to call componentWillUnmount on all937 // composites before this host node is removed from the tree. Therefore938 // we do an inner loop while we're still inside the host node.939 let node = root;940 while (true) {941 commitUnmount(finishedRoot, node, renderPriorityLevel);942 // Visit children because they may contain more composite or host nodes.943 // Skip portals because commitUnmount() currently visits them recursively.944 if (945 node.child !== null &&946 // If we use mutation we drill down into portals using commitUnmount above.947 // If we don't use mutation we drill down into portals here instead.948 (!supportsMutation || node.tag !== HostPortal)949 ) {950 node.child.return = node;951 node = node.child;952 continue;953 }954 if (node === root) {955 return;956 }957 while (node.sibling === null) {958 if (node.return === null || node.return === root) {959 return;960 }961 node = node.return;962 }963 node.sibling.return = node.return;964 node = node.sibling;965 }966}967function detachFiberMutation(fiber ) {968 // Cut off the return pointers to disconnect it from the tree. Ideally, we969 // should clear the child pointer of the parent alternate to let this970 // get GC:ed but we don't know which for sure which parent is the current971 // one so we'll settle for GC:ing the subtree of this child. This child972 // itself will be GC:ed when the parent updates the next time.973 // Note: we cannot null out sibling here, otherwise it can cause issues974 // with findDOMNode and how it requires the sibling field to carry out975 // traversal in a later effect. See PR #16820. We now clear the sibling976 // field after effects, see: detachFiberAfterEffects.977 //978 // Don't disconnect stateNode now; it will be detached in detachFiberAfterEffects.979 // It may be required if the current component is an error boundary,980 // and one of its descendants throws while unmounting a passive effect.981 fiber.alternate = null;982 fiber.child = null;983 fiber.dependencies = null;984 fiber.firstEffect = null;985 fiber.lastEffect = null;986 fiber.memoizedProps = null;987 fiber.memoizedState = null;988 fiber.pendingProps = null;989 fiber.return = null;990 fiber.updateQueue = null;991 if (__DEV__) {992 fiber._debugOwner = null;993 }994}995function emptyPortalContainer(current ) {996 if (!supportsPersistence) {997 return;998 }999 const portal 1000 1001 1002 1003 = current.stateNode;1004 const {containerInfo} = portal;1005 const emptyChildSet = createContainerChildSet(containerInfo);1006 replaceContainerChildren(containerInfo, emptyChildSet);1007}1008function commitContainer(finishedWork ) {1009 if (!supportsPersistence) {1010 return;1011 }1012 switch (finishedWork.tag) {1013 case ClassComponent:1014 case HostComponent:1015 case HostText:1016 case FundamentalComponent: {1017 return;1018 }1019 case HostRoot:1020 case HostPortal: {1021 const portalOrRoot 1022 1023 1024 1025 = finishedWork.stateNode;1026 const {containerInfo, pendingChildren} = portalOrRoot;1027 replaceContainerChildren(containerInfo, pendingChildren);1028 return;1029 }1030 }1031 invariant(1032 false,1033 'This unit of work tag should not have side-effects. This error is ' +1034 'likely caused by a bug in React. Please file an issue.',1035 );1036}1037function getHostParentFiber(fiber ) {1038 let parent = fiber.return;1039 while (parent !== null) {1040 if (isHostParent(parent)) {1041 return parent;1042 }1043 parent = parent.return;1044 }1045 invariant(1046 false,1047 'Expected to find a host parent. This error is likely caused by a bug ' +1048 'in React. Please file an issue.',1049 );1050}1051function isHostParent(fiber ) {1052 return (1053 fiber.tag === HostComponent ||1054 fiber.tag === HostRoot ||1055 fiber.tag === HostPortal1056 );1057}1058function getHostSibling(fiber ) {1059 // We're going to search forward into the tree until we find a sibling host1060 // node. Unfortunately, if multiple insertions are done in a row we have to1061 // search past them. This leads to exponential search for the next sibling.1062 // TODO: Find a more efficient way to do this.1063 let node = fiber;1064 siblings: while (true) {1065 // If we didn't find anything, let's try the next sibling.1066 while (node.sibling === null) {1067 if (node.return === null || isHostParent(node.return)) {1068 // If we pop out of the root or hit the parent the fiber we are the1069 // last sibling.1070 return null;1071 }1072 node = node.return;1073 }1074 node.sibling.return = node.return;1075 node = node.sibling;1076 while (1077 node.tag !== HostComponent &&1078 node.tag !== HostText &&1079 node.tag !== DehydratedFragment1080 ) {1081 // If it is not host node and, we might have a host node inside it.1082 // Try to search down until we find one.1083 if (node.flags & Placement) {1084 // If we don't have a child, try the siblings instead.1085 continue siblings;1086 }1087 // If we don't have a child, try the siblings instead.1088 // We also skip portals because they are not part of this host tree.1089 if (node.child === null || node.tag === HostPortal) {1090 continue siblings;1091 } else {1092 node.child.return = node;1093 node = node.child;1094 }1095 }1096 // Check if this host node is stable or about to be placed.1097 if (!(node.flags & Placement)) {1098 // Found it!1099 return node.stateNode;1100 }1101 }1102}1103function commitPlacement(finishedWork ) {1104 if (!supportsMutation) {1105 return;1106 }1107 // Recursively insert all host nodes into the parent.1108 const parentFiber = getHostParentFiber(finishedWork);1109 // Note: these two variables *must* always be updated together.1110 let parent;1111 let isContainer;1112 const parentStateNode = parentFiber.stateNode;1113 switch (parentFiber.tag) {1114 case HostComponent:1115 parent = parentStateNode;1116 isContainer = false;1117 break;1118 case HostRoot:1119 parent = parentStateNode.containerInfo;1120 isContainer = true;1121 break;1122 case HostPortal:1123 parent = parentStateNode.containerInfo;1124 isContainer = true;1125 break;1126 case FundamentalComponent:1127 if (enableFundamentalAPI) {1128 parent = parentStateNode.instance;1129 isContainer = false;1130 }1131 // eslint-disable-next-line-no-fallthrough1132 default:1133 invariant(1134 false,1135 'Invalid host parent fiber. This error is likely caused by a bug ' +1136 'in React. Please file an issue.',1137 );1138 }1139 if (parentFiber.flags & ContentReset) {1140 // Reset the text content of the parent before doing any insertions1141 resetTextContent(parent);1142 // Clear ContentReset from the effect tag1143 parentFiber.flags &= ~ContentReset;1144 }1145 const before = getHostSibling(finishedWork);1146 // We only have the top Fiber that was inserted but we need to recurse down its1147 // children to find all the terminal nodes.1148 if (isContainer) {1149 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);1150 } else {1151 insertOrAppendPlacementNode(finishedWork, before, parent);1152 }1153}1154function insertOrAppendPlacementNodeIntoContainer(1155 node ,1156 before ,1157 parent ,1158) {1159 const {tag} = node;1160 const isHost = tag === HostComponent || tag === HostText;1161 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1162 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1163 if (before) {1164 insertInContainerBefore(parent, stateNode, before);1165 } else {1166 appendChildToContainer(parent, stateNode);1167 }1168 } else if (tag === HostPortal) {1169 // If the insertion itself is a portal, then we don't want to traverse1170 // down its children. Instead, we'll get insertions from each child in1171 // the portal directly.1172 } else {1173 const child = node.child;1174 if (child !== null) {1175 insertOrAppendPlacementNodeIntoContainer(child, before, parent);1176 let sibling = child.sibling;1177 while (sibling !== null) {1178 insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);1179 sibling = sibling.sibling;1180 }1181 }1182 }1183}1184function insertOrAppendPlacementNode(1185 node ,1186 before ,1187 parent ,1188) {1189 const {tag} = node;1190 const isHost = tag === HostComponent || tag === HostText;1191 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1192 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1193 if (before) {1194 insertBefore(parent, stateNode, before);1195 } else {1196 appendChild(parent, stateNode);1197 }1198 } else if (tag === HostPortal) {1199 // If the insertion itself is a portal, then we don't want to traverse1200 // down its children. Instead, we'll get insertions from each child in1201 // the portal directly.1202 } else {1203 const child = node.child;1204 if (child !== null) {1205 insertOrAppendPlacementNode(child, before, parent);1206 let sibling = child.sibling;1207 while (sibling !== null) {1208 insertOrAppendPlacementNode(sibling, before, parent);1209 sibling = sibling.sibling;1210 }1211 }1212 }1213}1214function unmountHostComponents(1215 finishedRoot ,1216 current ,1217 renderPriorityLevel ,1218) {1219 // We only have the top Fiber that was deleted but we need to recurse down its1220 // children to find all the terminal nodes.1221 let node = current;1222 // Each iteration, currentParent is populated with node's host parent if not1223 // currentParentIsValid.1224 let currentParentIsValid = false;1225 // Note: these two variables *must* always be updated together.1226 let currentParent;1227 let currentParentIsContainer;1228 while (true) {1229 if (!currentParentIsValid) {1230 let parent = node.return;1231 findParent: while (true) {1232 invariant(1233 parent !== null,1234 'Expected to find a host parent. This error is likely caused by ' +1235 'a bug in React. Please file an issue.',1236 );1237 const parentStateNode = parent.stateNode;1238 switch (parent.tag) {1239 case HostComponent:1240 currentParent = parentStateNode;1241 currentParentIsContainer = false;1242 break findParent;1243 case HostRoot:1244 currentParent = parentStateNode.containerInfo;1245 currentParentIsContainer = true;1246 break findParent;1247 case HostPortal:1248 currentParent = parentStateNode.containerInfo;1249 currentParentIsContainer = true;1250 break findParent;1251 case FundamentalComponent:1252 if (enableFundamentalAPI) {1253 currentParent = parentStateNode.instance;1254 currentParentIsContainer = false;1255 }1256 }1257 parent = parent.return;1258 }1259 currentParentIsValid = true;1260 }1261 if (node.tag === HostComponent || node.tag === HostText) {1262 commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);1263 // After all the children have unmounted, it is now safe to remove the1264 // node from the tree.1265 if (currentParentIsContainer) {1266 removeChildFromContainer(1267 ((currentParent ) ),1268 (node.stateNode ),1269 );1270 } else {1271 removeChild(1272 ((currentParent ) ),1273 (node.stateNode ),1274 );1275 }1276 // Don't visit children because we already visited them.1277 } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {1278 const fundamentalNode = node.stateNode.instance;1279 commitNestedUnmounts(finishedRoot, node, renderPriorityLevel);1280 // After all the children have unmounted, it is now safe to remove the1281 // node from the tree.1282 if (currentParentIsContainer) {1283 removeChildFromContainer(1284 ((currentParent ) ),1285 (fundamentalNode ),1286 );1287 } else {1288 removeChild(1289 ((currentParent ) ),1290 (fundamentalNode ),1291 );1292 }1293 } else if (1294 enableSuspenseServerRenderer &&1295 node.tag === DehydratedFragment1296 ) {1297 if (enableSuspenseCallback) {1298 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1299 if (hydrationCallbacks !== null) {1300 const onDeleted = hydrationCallbacks.onDeleted;1301 if (onDeleted) {1302 onDeleted((node.stateNode ));1303 }1304 }1305 }1306 // Delete the dehydrated suspense boundary and all of its content.1307 if (currentParentIsContainer) {1308 clearSuspenseBoundaryFromContainer(1309 ((currentParent ) ),1310 (node.stateNode ),1311 );1312 } else {1313 clearSuspenseBoundary(1314 ((currentParent ) ),1315 (node.stateNode ),1316 );1317 }1318 } else if (node.tag === HostPortal) {1319 if (node.child !== null) {1320 // When we go into a portal, it becomes the parent to remove from.1321 // We will reassign it back when we pop the portal on the way up.1322 currentParent = node.stateNode.containerInfo;1323 currentParentIsContainer = true;1324 // Visit children because portals might contain host components.1325 node.child.return = node;1326 node = node.child;1327 continue;1328 }1329 } else {1330 commitUnmount(finishedRoot, node, renderPriorityLevel);1331 // Visit children because we may find more host components below.1332 if (node.child !== null) {1333 node.child.return = node;1334 node = node.child;1335 continue;1336 }1337 }1338 if (node === current) {1339 return;1340 }1341 while (node.sibling === null) {1342 if (node.return === null || node.return === current) {1343 return;1344 }1345 node = node.return;1346 if (node.tag === HostPortal) {1347 // When we go out of the portal, we need to restore the parent.1348 // Since we don't keep a stack of them, we will search for it.1349 currentParentIsValid = false;1350 }1351 }1352 node.sibling.return = node.return;1353 node = node.sibling;1354 }1355}1356function commitDeletion(1357 finishedRoot ,1358 current ,1359 renderPriorityLevel ,1360) {1361 if (supportsMutation) {1362 // Recursively delete all host nodes from the parent.1363 // Detach refs and call componentWillUnmount() on the whole subtree.1364 unmountHostComponents(finishedRoot, current, renderPriorityLevel);1365 } else {1366 // Detach refs and call componentWillUnmount() on the whole subtree.1367 commitNestedUnmounts(finishedRoot, current, renderPriorityLevel);1368 }1369 const alternate = current.alternate;1370 detachFiberMutation(current);1371 if (alternate !== null) {1372 detachFiberMutation(alternate);1373 }1374}1375function commitWork(current , finishedWork ) {1376 if (!supportsMutation) {1377 switch (finishedWork.tag) {1378 case FunctionComponent:1379 case ForwardRef:1380 case MemoComponent:1381 case SimpleMemoComponent:1382 case Block: {1383 // Layout effects are destroyed during the mutation phase so that all1384 // destroy functions for all fibers are called before any create functions.1385 // This prevents sibling component effects from interfering with each other,1386 // e.g. a destroy function in one component should never override a ref set1387 // by a create function in another component during the same commit.1388 if (1389 enableProfilerTimer &&1390 enableProfilerCommitHooks &&1391 finishedWork.mode & ProfileMode1392 ) {1393 try {1394 startLayoutEffectTimer();1395 commitHookEffectListUnmount(1396 HookLayout | HookHasEffect,1397 finishedWork,1398 );1399 } finally {1400 recordLayoutEffectDuration(finishedWork);1401 }1402 } else {1403 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1404 }1405 return;1406 }1407 case Profiler: {1408 return;1409 }1410 case SuspenseComponent: {1411 commitSuspenseComponent(finishedWork);1412 attachSuspenseRetryListeners(finishedWork);1413 return;1414 }1415 case SuspenseListComponent: {1416 attachSuspenseRetryListeners(finishedWork);1417 return;1418 }1419 case HostRoot: {1420 if (supportsHydration) {1421 const root = finishedWork.stateNode;1422 if (root.hydrate) {1423 // We've just hydrated. No need to hydrate again.1424 root.hydrate = false;1425 commitHydratedContainer(root.containerInfo);1426 }1427 }1428 break;1429 }1430 case OffscreenComponent:1431 case LegacyHiddenComponent: {1432 return;1433 }1434 }1435 commitContainer(finishedWork);1436 return;1437 }1438 switch (finishedWork.tag) {1439 case FunctionComponent:1440 case ForwardRef:1441 case MemoComponent:1442 case SimpleMemoComponent:1443 case Block: {1444 // Layout effects are destroyed during the mutation phase so that all1445 // destroy functions for all fibers are called before any create functions.1446 // This prevents sibling component effects from interfering with each other,1447 // e.g. a destroy function in one component should never override a ref set1448 // by a create function in another component during the same commit.1449 if (1450 enableProfilerTimer &&1451 enableProfilerCommitHooks &&1452 finishedWork.mode & ProfileMode1453 ) {1454 try {1455 startLayoutEffectTimer();1456 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1457 } finally {1458 recordLayoutEffectDuration(finishedWork);1459 }1460 } else {1461 commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);1462 }1463 return;1464 }1465 case ClassComponent: {1466 return;1467 }1468 case HostComponent: {1469 const instance = finishedWork.stateNode;1470 if (instance != null) {1471 // Commit the work prepared earlier.1472 const newProps = finishedWork.memoizedProps;1473 // For hydration we reuse the update path but we treat the oldProps1474 // as the newProps. The updatePayload will contain the real change in1475 // this case.1476 const oldProps = current !== null ? current.memoizedProps : newProps;1477 const type = finishedWork.type;1478 // TODO: Type the updateQueue to be specific to host components.1479 const updatePayload = (finishedWork.updateQueue );1480 finishedWork.updateQueue = null;1481 if (updatePayload !== null) {1482 commitUpdate(1483 instance,1484 updatePayload,1485 type,1486 oldProps,1487 newProps,1488 finishedWork,1489 );1490 }1491 }1492 return;1493 }1494 case HostText: {1495 invariant(1496 finishedWork.stateNode !== null,1497 'This should have a text node initialized. This error is likely ' +1498 'caused by a bug in React. Please file an issue.',1499 );1500 const textInstance = finishedWork.stateNode;1501 const newText = finishedWork.memoizedProps;1502 // For hydration we reuse the update path but we treat the oldProps1503 // as the newProps. The updatePayload will contain the real change in1504 // this case.1505 const oldText =1506 current !== null ? current.memoizedProps : newText;1507 commitTextUpdate(textInstance, oldText, newText);1508 return;1509 }1510 case HostRoot: {1511 if (supportsHydration) {1512 const root = finishedWork.stateNode;1513 if (root.hydrate) {1514 // We've just hydrated. No need to hydrate again.1515 root.hydrate = false;1516 commitHydratedContainer(root.containerInfo);1517 }1518 }1519 return;1520 }1521 case Profiler: {1522 return;1523 }1524 case SuspenseComponent: {1525 commitSuspenseComponent(finishedWork);1526 attachSuspenseRetryListeners(finishedWork);1527 return;1528 }1529 case SuspenseListComponent: {1530 attachSuspenseRetryListeners(finishedWork);1531 return;1532 }1533 case IncompleteClassComponent: {1534 return;1535 }1536 case FundamentalComponent: {1537 if (enableFundamentalAPI) {1538 const fundamentalInstance = finishedWork.stateNode;1539 updateFundamentalComponent(fundamentalInstance);1540 return;1541 }1542 break;1543 }1544 case ScopeComponent: {1545 if (enableScopeAPI) {1546 const scopeInstance = finishedWork.stateNode;1547 prepareScopeUpdate(scopeInstance, finishedWork);1548 return;1549 }1550 break;1551 }1552 case OffscreenComponent:1553 case LegacyHiddenComponent: {1554 const newState = finishedWork.memoizedState;1555 const isHidden = newState !== null;1556 hideOrUnhideAllChildren(finishedWork, isHidden);1557 return;1558 }1559 }1560 invariant(1561 false,1562 'This unit of work tag should not have side-effects. This error is ' +1563 'likely caused by a bug in React. Please file an issue.',1564 );1565}1566function commitSuspenseComponent(finishedWork ) {1567 const newState = finishedWork.memoizedState;1568 if (newState !== null) {1569 markCommitTimeOfFallback();1570 if (supportsMutation) {1571 // Hide the Offscreen component that contains the primary children. TODO:1572 // Ideally, this effect would have been scheduled on the Offscreen fiber1573 // itself. That's how unhiding works: the Offscreen component schedules an1574 // effect on itself. However, in this case, the component didn't complete,1575 // so the fiber was never added to the effect list in the normal path. We1576 // could have appended it to the effect list in the Suspense component's1577 // second pass, but doing it this way is less complicated. This would be1578 // simpler if we got rid of the effect list and traversed the tree, like1579 // we're planning to do.1580 const primaryChildParent = (finishedWork.child );1581 hideOrUnhideAllChildren(primaryChildParent, true);1582 }1583 }1584 if (enableSuspenseCallback && newState !== null) {1585 const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;1586 if (typeof suspenseCallback === 'function') {1587 const wakeables = (finishedWork.updateQueue );1588 if (wakeables !== null) {1589 suspenseCallback(new Set(wakeables));1590 }1591 } else if (__DEV__) {1592 if (suspenseCallback !== undefined) {1593 console.error('Unexpected type for suspenseCallback.');1594 }1595 }1596 }1597}1598function commitSuspenseHydrationCallbacks(1599 finishedRoot ,1600 finishedWork ,1601) {1602 if (!supportsHydration) {1603 return;1604 }1605 const newState = finishedWork.memoizedState;1606 if (newState === null) {1607 const current = finishedWork.alternate;1608 if (current !== null) {1609 const prevState = current.memoizedState;1610 if (prevState !== null) {1611 const suspenseInstance = prevState.dehydrated;1612 if (suspenseInstance !== null) {1613 commitHydratedSuspenseInstance(suspenseInstance);1614 if (enableSuspenseCallback) {1615 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1616 if (hydrationCallbacks !== null) {1617 const onHydrated = hydrationCallbacks.onHydrated;1618 if (onHydrated) {1619 onHydrated(suspenseInstance);1620 }1621 }1622 }1623 }1624 }1625 }1626 }1627}1628function attachSuspenseRetryListeners(finishedWork ) {1629 // If this boundary just timed out, then it will have a set of wakeables.1630 // For each wakeable, attach a listener so that when it resolves, React1631 // attempts to re-render the boundary in the primary (pre-timeout) state.1632 const wakeables = (finishedWork.updateQueue );1633 if (wakeables !== null) {1634 finishedWork.updateQueue = null;1635 let retryCache = finishedWork.stateNode;1636 if (retryCache === null) {1637 retryCache = finishedWork.stateNode = new PossiblyWeakSet();1638 }1639 wakeables.forEach(wakeable => {1640 // Memoize using the boundary fiber to prevent redundant listeners.1641 let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);1642 if (!retryCache.has(wakeable)) {1643 if (enableSchedulerTracing) {1644 if (wakeable.__reactDoNotTraceInteractions !== true) {1645 retry = Schedule_tracing_wrap(retry);1646 }1647 }1648 retryCache.add(wakeable);1649 wakeable.then(retry, retry);1650 }1651 });1652 }1653}1654// This function detects when a Suspense boundary goes from visible to hidden.1655// It returns false if the boundary is already hidden.1656// TODO: Use an effect tag.1657export function isSuspenseBoundaryBeingHidden(1658 current ,1659 finishedWork ,1660) {1661 if (current !== null) {1662 const oldState = current.memoizedState;1663 if (oldState === null || oldState.dehydrated !== null) {1664 const newState = finishedWork.memoizedState;1665 return newState !== null && newState.dehydrated === null;1666 }1667 }1668 return false;1669}1670function commitResetTextContent(current ) {1671 if (!supportsMutation) {1672 return;1673 }1674 resetTextContent(current.stateNode);1675}1676export {1677 commitBeforeMutationLifeCycles,1678 commitResetTextContent,1679 commitPlacement,1680 commitDeletion,1681 commitWork,1682 commitLifeCycles,1683 commitAttachRef,1684 commitDetachRef,...
FiberCommitWork.js
Source:FiberCommitWork.js
1import{2 HostRoot,3 HostText,4 HostComponent,5 FunctionComponent,6 BeforeMutationMask,7 MutationMask,8 NoFlags,9 ContentReset,10 Placement,11 Update,12 PlacementAndUpdate,13 LayoutMask,14 SuspenseComponent,15 OffscreenComponent,16 ChildDeletion,17 PassiveMask,18 Passive,19 HookPassive,20 HookHasEffect21} from '@Jeact/shared/Constants';22import {23 resolveRetryWakeable24} from '@Jeact/vDOM/FiberWorkLoop';25import {26 updateFiberProps,27 detachDeletedInstance28} from '@Jeact/vDOM/DOMComponentTree';29import { updateDOMProperties } from '@Jeact/vDOM/DOMComponent'30let nextEffect = null;31export function commitBeforeMutationEffects(firstChild){32 nextEffect = firstChild;33 commitBeforeMutationEffects_begin();34}35function commitBeforeMutationEffects_begin(){36 while(nextEffect !== null){37 const fiber = nextEffect;38 const deletions = fiber.deletions;39 if(deletions !== null){40 for (let i = 0; i < deletions.length; i++){41 const deletion = deletions[i];42 // commitBeforeMutationEffectsDeletion(deletion);43 }44 }45 const child = fiber.child;46 if(47 (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&48 child !== null49 ){50 nextEffect = child;51 } else {52 commitBeforeMutationEffects_complete();53 }54 }55}56function commitBeforeMutationEffects_complete(){57 while(nextEffect !== null){58 const fiber = nextEffect;59 commitBeforeMutationEffectsOnFiber(fiber);60 const sibling = fiber.sibling;61 if(sibling !== null){62 nextEffect = sibling;63 return;64 }65 nextEffect = fiber.return;66 }67}68function commitBeforeMutationEffectsOnFiber(finishedWork){69 const current = finishedWork.alternate;70 const flags = finishedWork.flags;71}72function commitHookEffectListUnmount(73 flags, 74 finishedWork, 75 nearestMountedAncestor76){77 const updateQueue = finishedWork.updateQueue;78 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;79 if (lastEffect !== null){80 const firstEffect = lastEffect.next;81 let effect = firstEffect;82 do {83 if ((effect.tag & flags) === flags){84 // unmount85 const destroy = effect.destroy;86 effect.destroy = undefined;87 if (destroy !== undefined){88 destroy();89 }90 }91 effect = effect.next;92 } while (effect !== firstEffect);93 }94}95function commitHookEffectListMount(tag, finishedWork){96 const updateQueue = finishedWork.updateQueue;97 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;98 if (lastEffect !== null){99 const firstEffect = lastEffect.next;100 let effect = firstEffect;101 do {102 if ((effect.tag & tag) === tag){103 // Mount104 const create = effect.create;105 effect.destroy = create();106 }107 effect = effect.next;108 } while(effect !== firstEffect);109 }110}111function attachSuspenseRetryListeners(finishedWork){112 const wakeables = finishedWork.updateQueue;113 if (wakeables !== null){114 finishedWork.updateQueue = null;115 let retryCache = finishedWork.stateNode;116 if(retryCache === null){117 retryCache = finishedWork.stateNode = new WeakSet()118 }119 wakeables.forEach(wakeable => {120 const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);121 if(!retryCache.has(wakeable)){122 retryCache.add(wakeable);123 wakeable.then(retry, retry); 124 }125 })126 }127}128export function commitMutationEffects(root,firstChild){129 nextEffect = firstChild;130 commitMutationEffects_begin(root);131}132function commitMutationEffects_begin(root){133 while(nextEffect !== null){134 const fiber = nextEffect;135 const deletions = fiber.deletions;136 if(deletions !==null ){137 for (let i = 0; i < deletions.length; i++){138 const childToDelete = deletions[i];139 commitDeletion(root, childToDelete, fiber);140 }141 }142 const child = fiber.child;143 if((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null){144 nextEffect = child;145 } else {146 commitMutationEffects_complete(root);147 }148 }149}150function commitMutationEffects_complete(root){151 while(nextEffect !== null){152 const fiber = nextEffect;153 commitMutationEffectsOnFiber(fiber, root);154 const sibling = fiber.sibling;155 if (sibling !== null){156 nextEffect = sibling;157 return;158 }159 nextEffect = fiber.return;160 }161}162function commitMutationEffectsOnFiber(finishedWork, root){163 const flags = finishedWork.flags;164 const primaryFlags = flags & (Placement | Update);165 switch(primaryFlags){166 case Placement:{167 commitPlacement(finishedWork);168 finishedWork.flags &= ~Placement;169 break;170 }171 case PlacementAndUpdate:{172 // Placement173 commitPlacement(finishedWork);174 finishedWork.flags &= ~Placement;175 //Update176 const current = finishedWork.alternate;177 commitWork(current, finishedWork);178 break;179 }180 case Update:{181 const current = finishedWork.alternate;182 commitWork(current, finishedWork);183 break;184 }185 }186}187export function commitLayoutEffects(finishedWork, root, committedLanes){188 nextEffect = finishedWork;189 commitLayoutEffects_begin(finishedWork, root, committedLanes);190}191function commitLayoutEffects_begin(subtreeRoot, root, committedLanes){192 while (nextEffect !== null){193 const fiber = nextEffect;194 const firstChild = fiber.child;195 if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null){196 nextEffect = firstChild;197 } else {198 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);199 }200 }201}202function commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes){203 while (nextEffect !== null){204 const fiber = nextEffect;205 if((fiber.flags & LayoutMask) !== NoFlags){206 const current = fiber.alternate;207 commitLayoutEffectOnFiber(root, current, fiber, committedLanes);208 }209 if (fiber === subtreeRoot){210 nextEffect = null;211 return;212 }213 const sibling = fiber.sibling;214 if (sibling !== null){215 nextEffect = sibling;216 return;217 }218 nextEffect = fiber.return;219 }220}221export function commitPassiveMountEffects(root, finishedWork){222 nextEffect = finishedWork;223 commitPassiveMountEffects_begin(finishedWork, root);224}225function commitPassiveMountEffects_begin(subtreeRoot, root){226 while(nextEffect!== null){227 const fiber = nextEffect;228 const firstChild = fiber.child;229 if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null){230 nextEffect = firstChild;231 } else {232 commitPassiveMountEffects_complete(subtreeRoot, root);233 }234 }235}236function commitPassiveMountEffects_complete(subtreeRoot, root){237 while(nextEffect !== null){238 const fiber = nextEffect;239 if ((fiber.flags & Passive) !== NoFlags){240 commitPassiveMountEffectsOnFiber(root, fiber);241 }242 if (fiber === subtreeRoot){243 nextEffect = null;244 return ;245 }246 const sibling = fiber.sibling;247 if (sibling !== null){248 nextEffect = sibling;249 return;250 }251 nextEffect = fiber.return;252 }253}254function commitPassiveMountOnFiber(finishedRoot, finishedWork){255 switch(finishedWork.tag){256 case FunctionComponent:{257 commitHookEffectListMount(258 HookPassive | HookHasEffect, 259 finishedWork260 );261 break;262 }263 }264}265function commitLayoutEffectOnFiber(finishedRoot, current, finishedWork, committedLanes){266 if ((finishedWork.flags & Update) !== NoFlags){267 switch(finishedWork.tag){268 default:269 break;270 }271 }272}273export function commitPassiveUnmountEffects(firstChild){274 nextEffect = firstChild;275 commitPassiveUnmountEffects_begin();276}277function commitPassiveUnmountEffects_begin(){278 while(nextEffect !== null){279 const fiber = nextEffect;280 const child = fiber.child;281 if ((nextEffect.flags & ChildDeletion) !== NoFlags){282 const deletions = fiber.deletions;283 if(deletions !== null){284 for (let i = 0; i < deletions.length; i++){285 const fiberToDel = deletions[i];286 nextEffect = fiberToDel;287 commitPassiveUnmountEffectsInDelTree_begin(288 fiberToDel,289 fiber,290 )291 }292 }293 }294 if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null){295 nextEffect = child;296 } else {297 commitPassiveUnmountEffects_complete();298 }299 }300}301function commitPassiveUnmountEffects_complete(){302 while (nextEffect !== null){303 const fiber = nextEffect;304 if ((fiber.flags & Passive) !== NoFlags){305 commitPassiveUnmountOnFiber(fiber);306 }307 const sibling = fiber.sibling;308 if (sibling !== null){309 nextEffect = sibling;310 return;311 }312 nextEffect = fiber.return;313 }314}315function commitPassiveUnmountOnFiber(finishedWork){316 switch(finishedWork.tag){317 case FunctionComponent:{318 commitHookEffectListUnmount(319 HookPassive | HookHasEffect,320 finishedWork,321 finishedWork.return322 )323 }324 }325}326function commitPassiveUnmountEffectsInDelTree_begin(327 deletedSubtreeRoot,328 nearestMountedAncestor329){330 while (nextEffect !== null){331 const fiber = nextEffect;332 333 commitPassiveUnmountEffectsInDelTreeOnFiber(334 fiber, 335 nearestMountedAncestor336 );337 const child = fiber.child;338 if (child !== null){339 nextEffect = child;340 } else {341 commitPassiveUnmountEffectsInDelTree_complete(342 deletedSubtreeRoot343 )344 }345 }346}347function commitPassiveUnmountEffectsInDelTree_complete(deletedSubtreeRoot){348 while (nextEffect !== null){349 const fiber = nextEffect;350 const sibling = fiber.sibling;351 const returnFiber = fiber.return;352 if (fiber === deletedSubtreeRoot){353 detachFiberAfterEffects(fiber);354 nextEffect = null;355 return;356 }357 if (sibling !== null){358 nextEffect = sibling;359 return;360 }361 nextEffect = returnFiber;362 }363}364function commitPassiveUnmountEffectsInDelTreeOnFiber(365 current, nearestMountedAncestor366){367 switch(current.tag){368 case FunctionComponent:{369 commitHookEffectListUnmount(370 HookPassive,371 current,372 nearestMountedAncestor373 );374 break;375 }376 }377}378function commitUnmount(finishedRoot, current, nearestMountedAncestor){379 switch(current.tag){380 case FunctionComponent:381 case HostComponent:382 return;383 }384}385function commitNestedUnmounts(finishedRoot, root, nearestMountedAncestor){386 let node = root;387 while(true){388 commitUnmount(finishedRoot, node, nearestMountedAncestor);389 if(node.child !== null){390 node.child.return = node;391 node = node.child;392 continue;393 }394 if (node === root){395 return;396 }397 while (node.sibling === null){398 if (node.return === null || node.return === root){399 return;400 }401 node = node.return;402 }403 node.sibling.return = node.return;404 node = node.sibling;405 }406}407function detachFiberMutation(fiber){408 const alternate = fiber.alternate;409 if (alternate !== null){410 alternate.return = null;411 }412 fiber.return = null;413}414function detachFiberAfterEffects(fiber){415 const alternate = fiber.alternate;416 if (alternate !== null){417 fiber.alternate = null;418 detachFiberAfterEffects(alternate);419 }420 fiber.child = null;421 fiber.deletions = null;422 fiber.sibling = null;423 if (fiber.tag === HostComponent){424 if(fiber.stateNode !== null){425 detachDeletedInstance(fiber.stateNode);426 }427 }428 fiber.stateNode = null;429}430function toggleAllChildren(finishedWork, isHidden){431 let hostSubtreeRoot = null;432 let node = finishedWork;433 while(true){434 if (node.tag === HostComponent){435 if(hostSubtreeRoot === null){436 hostSubtreeRoot = node;437 const instance = node.stateNode;438 if (isHidden){439 const style = instance.style;440 if(typeof style.setProperty === 'function'){441 style.setProperty('display', 'none', 'important');442 } else {443 style.display = 'none';444 }445 } else {446 instance.style.removeProperty('display');447 }448 }449 } else if (node.tag === HostText){450 if(hostSubtreeRoot === null){451 const instance = node.stateNode;452 if (isHidden){453 instance.nodeValue ='';454 } else {455 instance.nodeValue = node.memoizedProps;456 }457 }458 } else if(459 node.tag === OffscreenComponent &&460 node.memoizedState !== null &&461 node !== finishedWork462 ){463 debugger;464 } else if (node.child !== null){465 node.child.return = node;466 node = node.child;467 continue;468 }469 if (node == finishedWork){470 return;471 }472 while(node.sibling === null){473 if (node.return === null || node.return === finishedWork){474 return;475 }476 if(hostSubtreeRoot === node){477 hostSubtreeRoot = null;478 }479 node = node.return;480 }481 if(hostSubtreeRoot === node){482 hostSubtreeRoot = null;483 }484 node.sibling.return = node.return;485 node = node.sibling;486 }487}488function getHostParentFiber(fiber){489 let parent = fiber.return;490 while (parent!== null){491 if (isHostParent(parent)){492 return parent;493 }494 parent = parent.return;495 }496}497function isHostParent(fiber){498 return (499 fiber.tag === HostRoot ||500 fiber.tag === HostComponent501 );502}503function getHostSibling(fiber){504 let node = fiber;505 siblings: while (true){506 while(node.sibling === null){507 if (node.return === null || isHostParent(node.return)){508 return null;509 }510 node = node.return;511 }512 node.sibling.return = node.return;513 node = node.sibling;514 while (515 node.tag !== HostComponent &&516 node.tag !== HostText517 ){518 if (node.flags & Placement){519 continue siblings;520 }521 if (node.child === null){522 continue siblings;523 } else {524 node.child.return = node;525 node = node.child;526 }527 }528 if (!(node.flags & Placement)){529 return node.stateNode;530 }531 }532}533function commitPlacement(finishedWork){534 const parentFiber = getHostParentFiber(finishedWork);535 let parent;536 let isContainer;537 const parentStateNode = parentFiber.stateNode;538 switch(parentFiber.tag){539 case HostComponent:540 parent = parentStateNode;541 isContainer = false;542 break;543 case HostRoot:544 parent = parentStateNode.container;545 isContainer = true;546 break;547 default:548 console.error('Unknown parentFiber', parentFiber.tag);549 }550 const before = getHostSibling(finishedWork);551 if(isContainer){552 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);553 } else {554 insertOrAppendPlacementNode(finishedWork, before, parent);555 }556}557function insertOrAppendPlacementNodeIntoContainer(node, before, parent){558 const tag = node.tag;559 const isHost = tag === HostComponent || tag === HostText;560 if (isHost){561 const stateNode =node.stateNode;562 if (before){563 parent.insertBefore(stateNode, before)564 } else {565 parent.append(stateNode);566 }567 } else {568 const child = node.child;569 if (child !== null){570 insertOrAppendPlacementNodeIntoContainer(child, before, parent);571 let sibling = child.sibling;572 while (sibling !== null ){573 insertOrAppendPlacementNodeIntoContainer(574 sibling, 575 before, 576 parent);577 sibling = sibling.sibling;578 }579 }580 }581}582function insertOrAppendPlacementNode(node, before, parent){583 const tag = node.tag;584 const isHost = tag === HostComponent || tag === HostText;585 if (isHost){586 const stateNode = node.stateNode;587 if (before){588 parent.insertBefore(stateNode, before);589 } else {590 parent.append(stateNode);591 }592 } else {593 const child = node.child;594 if(child !== null){595 insertOrAppendPlacementNode(child, before, parent);596 let sibling = child.sibling;597 while(sibling !== null){598 insertOrAppendPlacementNode(sibling, before, parent);599 sibling = sibling.sibling;600 }601 }602 }603}604function unmountHostComponents(finishedRoot, current, nearestMountedAncestor){605 let node = current;606 let currentParentIsValid = false;607 let currentParent;608 let currentParentIsContainer;609 while(true){610 if (!currentParentIsValid){611 let parent = node.return;612 findParent: while(true){613 const parentStateNode = parent.stateNode;614 switch(parent.tag){615 case HostComponent:616 currentParent = parentStateNode;617 currentParentIsContainer = false;618 break findParent;619 case HostRoot:620 currentParent = parentStateNode.container;621 currentParentIsContainer = true;622 break findParent;623 }624 parent = parent.return;625 }626 currentParentIsValid = true;627 }628 if (node.tag === HostComponent || node.tag === HostText){629 commitNestedUnmounts(finishedRoot, node, nearestMountedAncestor);630 currentParent.removeChild(node.stateNode);631 } else {632 commitUnmount(finishedRoot, node, nearestMountedAncestor);633 if (node.child !== null){634 node.child.return = node;635 node = node.child;636 continue;637 }638 }639 if (node === current){640 return;641 }642 while (node.sibling === null){643 if (node.return === null || node.return === current){644 return;645 }646 node = node.return;647 }648 node.sibling.return = node.return;649 node = node.sibling;650 }651}652function commitDeletion(finishedRoot, current, nearestMountedAncestor){653 unmountHostComponents(finishedRoot, current, nearestMountedAncestor);654 detachFiberMutation(current);655}656function commitWork(current, finishedWork){657 switch(finishedWork.tag){658 case HostComponent:{659 const instance = finishedWork.stateNode;660 if (instance !== null){661 const newProps = finishedWork.memoizedProps;662 const oldProps = current !== null ? current.memoizedProps : newProps;663 const type = finishedWork.type;664 const updatePayload = finishedWork.updateQueue;665 finishedWork.updateQueue = null;666 if (updatePayload !== null){667 commitUpdate(668 instance,669 updatePayload,670 type,671 oldProps,672 newProps,673 finishedWork,674 )675 }676 }677 return;678 }679 case SuspenseComponent:{680 commitSuspenseComponent(finishedWork);681 attachSuspenseRetryListeners(finishedWork);682 return;683 }684 case OffscreenComponent:{685 const newState = finishedWork.memoizedState;686 const isHidden = newState !== null;687 toggleAllChildren(finishedWork, isHidden);688 return;689 }690 case HostText:{691 const textInstance = finishedWork.stateNode;692 const newText = finishedWork.memoizedProps;693 textInstance.nodeValue = newText;694 }695 }696}697function commitSuspenseComponent(finishedWork){698 const newState = finishedWork.memoizedState;699 if (newState !== null){700 const primaryChildParent = finishedWork.child;701 //hideOrUnhideAllChildren()702 toggleAllChildren(primaryChildParent, true); 703 }704}705function commitUpdate(706 domElement,707 updatePayload,708 type,709 oldProps,710 newProps,711 internalInstanceHandle712){713 updateFiberProps(domElement, newProps);714 updateDOMProperties(domElement, updatePayload);...
FiberWorkLoop.js
Source:FiberWorkLoop.js
...420 markRootUpdated(root, retryLane, eventTime);421 ensureRootIsScheduled(root, eventTime);422 }423}424export function resolveRetryWakeable(boundaryFiber, wakeable){425 let retryCache = boundaryFiber.stateNode;426 if(retryCache !== null){427 retryCache.delete(wakeable);428 }429 retryTimedOutBoundary(boundaryFiber);...
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.screenshot({ path: `example.png` });7 await browser.close();8})();9const { resolveRetryWakeable } = require('playwright');10module.exports = {11 use: {12 viewport: { width: 800, height: 600 },13 },14};15const { resolveRetryWakeable } = require('playwright');16module.exports = {17 use: {18 viewport: { width: 800, height: 600 },19 },20};21const { resolveRetryWakeable } = require('playwright');22module.exports = {23 use: {24 viewport: { width: 800, height: 600 },25 },26};27const { resolveRetryWakeable } = require('playwright');28module.exports = {29 use: {30 viewport: { width: 800, height: 600 },31 },32};33const { resolveRetryWakeable } = require('playwright');34module.exports = {35 use: {36 viewport: { width: 800, height: 600 },37 },38};
Using AI Code Generation
1const { resolveRetryWakeable } = require("@playwright/test/lib/server/wakeable");2const { chromium } = require("playwright");3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const elementHandle = await page.$("text=Get Started");8 const wakeable = await elementHandle._retry();9 const retry = await resolveRetryWakeable(wakeable);10 console.log(retry);11 await browser.close();12})();
Using AI Code Generation
1const { resolveRetryWakeable } = require('playwright-core/lib/server/wakeable');2const { chromium } = require('playwright-core');3const browser = await chromium.launch();4const page = await browser.newPage();5await page.waitForSelector('a');6const wakeable = await page.waitForSelector('a');7const element = await resolveRetryWakeable(wakeable);8console.log(element);9await browser.close();10ElementHandle {11 _context: ExecutionContext {12 _page: Page {13 _browserContext: BrowserContext {14 _browser: Browser {15 _connection: Connection {16 _ws: WebSocket {…},17 _callbacks: Map {},18 _sessions: Map {},19 _sessionsIds: Map {},20 _dispatchQueue: Promise {},21 _crBrowserSessionPromise: Promise {},22 _crBrowserSessionClosePromise: Promise {},
Using AI Code Generation
1const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');2const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');3const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');4const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');5const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');6const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');7const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');8const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');9const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');10const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');11const { resolveRetryWakeable } = require('playwright/lib/internal/utils/utils');12const { resolve
Using AI Code Generation
1const {InternalHelper} = require('playwright/lib/server/helper.js');2const helper = new InternalHelper();3const wakeable = helper.resolveRetryWakeable(1000);4wakeable.then((value) => {5 console.log(value);6});7const {InternalHelper} = require('playwright/lib/server/helper.js');8const helper = new InternalHelper();9const wakeable = helper.resolveRetryWakeable(1000);10wakeable.then((value) => {11 console.log(value);12});13const {InternalHelper} = require('playwright/lib/server/helper.js');14const helper = new InternalHelper();15const wakeable = helper.resolveRetryWakeable(1000);16wakeable.then((value) => {17 console.log(value);18});19const {InternalHelper} = require('playwright/lib/server/helper.js');20const helper = new InternalHelper();21const wakeable = helper.resolveRetryWakeable(1000);22wakeable.then((value) => {23 console.log(value);24});25const {InternalHelper} = require('playwright/lib/server/helper.js');26const helper = new InternalHelper();27const wakeable = helper.resolveRetryWakeable(1000);28wakeable.then((value) => {29 console.log(value);30});31const {InternalHelper} = require('playwright/lib/server/helper.js');32const helper = new InternalHelper();33const wakeable = helper.resolveRetryWakeable(1000);34wakeable.then((value) => {35 console.log(value);36});37const {InternalHelper} = require('playwright
Using AI Code Generation
1const { resolveRetryWakeable } = require('playwright/lib/utils/utils');2const { PlaywrightTest } = require('@playwright/test');3const { test } = PlaywrightTest;4test('test', async ({ page }) => {5 await resolveRetryWakeable(() => page.isVisible('css=div'), false);6 await page.click('css=div');7});8Example: Wait for the element to be visible using waitForSelector()9const { test } = require('@playwright/test');10test('test', async ({ page }) => {11 await page.waitForSelector('css=div');12 await page.click('css=div');13});14Example: Wait for the element to be visible using waitForFunction()15const { test } = require('@playwright/test');16test('test', async ({ page }) => {17 await page.waitForFunction(() => document.querySelector('div').offsetWidth > 0);18 await page.click('css=div');19});20Example: Wait for the element to be visible using waitForTimeout()21const { test } = require('@playwright/test');22test('test', async ({ page }) => {23 await page.waitForTimeout(1000);24 await page.click('css=div');25});26Example: Wait for the element to be visible using waitForResponse()27const { test } = require('@playwright/test');28test('test', async ({ page }) => {29 await page.click('css=div');30});31Example: Wait for the element to be visible using waitForRequest()32const { test } = require('@playwright/test');33test('test', async ({ page }) => {34 await page.click('css=div');35});36Example: Wait for the element to be visible using waitForEvent()37const { test } = require('@playwright/test');
Using AI Code Generation
1const { resolveRetryWakeable } = require('playwright/lib/utils/utils');2const { TimeoutError } = require('playwright/lib/errors');3const { createTestServer } = require('playwright/lib/utils/testserver');4(async () => {5 const server = await createTestServer();6 server.setRoute('/sleep', async (req, res) => {7 res.setHeader('Content-Type', 'text/plain');8 res.write('hello ');9 await new Promise(f => setTimeout(f, 1000));10 res.end('world');11 });12 const url = server.PREFIX + '/sleep';13 const wakeable = new Promise(f => setTimeout(f, 1000));14 const timeout = 100;15 try {16 await resolveRetryWakeable(wakeable, async () => {17 await page.goto(url);18 }, timeout);19 } catch (e) {20 if (e instanceof TimeoutError)21 console.log('Error: Timeout');22 }23})();
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!