Best JavaScript code snippet using playwright-internal
ReactFiberCommitWork.old.js
Source:ReactFiberCommitWork.old.js
...464 beforeActiveInstanceBlur(deletion);465 }466 }467}468function commitHookEffectListUnmount(469 flags: HookFlags,470 finishedWork: Fiber,471 nearestMountedAncestor: Fiber | null,472) {473 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);474 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;475 if (lastEffect !== null) {476 const firstEffect = lastEffect.next;477 let effect = firstEffect;478 do {479 if ((effect.tag & flags) === flags) {480 // Unmount481 const destroy = effect.destroy;482 effect.destroy = undefined;483 if (destroy !== undefined) {484 safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);485 }486 }487 effect = effect.next;488 } while (effect !== firstEffect);489 }490}491function commitHookEffectListMount(tag: HookFlags, finishedWork: Fiber) {492 const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);493 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;494 if (lastEffect !== null) {495 const firstEffect = lastEffect.next;496 let effect = firstEffect;497 do {498 if ((effect.tag & tag) === tag) {499 // Mount500 const create = effect.create;501 effect.destroy = create();502 if (__DEV__) {503 const destroy = effect.destroy;504 if (destroy !== undefined && typeof destroy !== 'function') {505 let hookName;506 if ((effect.tag & HookLayout) !== NoFlags) {507 hookName = 'useLayoutEffect';508 } else if ((effect.tag & HookInsertion) !== NoFlags) {509 hookName = 'useInsertionEffect';510 } else {511 hookName = 'useEffect';512 }513 let addendum;514 if (destroy === null) {515 addendum =516 ' You returned null. If your effect does not require clean ' +517 'up, return undefined (or nothing).';518 } else if (typeof destroy.then === 'function') {519 addendum =520 '\n\nIt looks like you wrote ' +521 hookName +522 '(async () => ...) or returned a Promise. ' +523 'Instead, write the async function inside your effect ' +524 'and call it immediately:\n\n' +525 hookName +526 '(() => {\n' +527 ' async function fetchData() {\n' +528 ' // You can await here\n' +529 ' const response = await MyAPI.getData(someId);\n' +530 ' // ...\n' +531 ' }\n' +532 ' fetchData();\n' +533 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +534 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';535 } else {536 addendum = ' You returned: ' + destroy;537 }538 console.error(539 '%s must not return anything besides a function, ' +540 'which is used for clean-up.%s',541 hookName,542 addendum,543 );544 }545 }546 }547 effect = effect.next;548 } while (effect !== firstEffect);549 }550}551export function commitPassiveEffectDurations(552 finishedRoot: FiberRoot,553 finishedWork: Fiber,554): void {555 if (enableProfilerTimer && enableProfilerCommitHooks) {556 // Only Profilers with work in their subtree will have an Update effect scheduled.557 if ((finishedWork.flags & Update) !== NoFlags) {558 switch (finishedWork.tag) {559 case Profiler: {560 const {passiveEffectDuration} = finishedWork.stateNode;561 const {id, onPostCommit} = finishedWork.memoizedProps;562 // This value will still reflect the previous commit phase.563 // It does not get reset until the start of the next commit phase.564 const commitTime = getCommitTime();565 let phase = finishedWork.alternate === null ? 'mount' : 'update';566 if (enableProfilerNestedUpdatePhase) {567 if (isCurrentUpdateNested()) {568 phase = 'nested-update';569 }570 }571 if (typeof onPostCommit === 'function') {572 onPostCommit(id, phase, passiveEffectDuration, commitTime);573 }574 // Bubble times to the next nearest ancestor Profiler.575 // After we process that Profiler, we'll bubble further up.576 let parentFiber = finishedWork.return;577 outer: while (parentFiber !== null) {578 switch (parentFiber.tag) {579 case HostRoot:580 const root = parentFiber.stateNode;581 root.passiveEffectDuration += passiveEffectDuration;582 break outer;583 case Profiler:584 const parentStateNode = parentFiber.stateNode;585 parentStateNode.passiveEffectDuration += passiveEffectDuration;586 break outer;587 }588 parentFiber = parentFiber.return;589 }590 break;591 }592 default:593 break;594 }595 }596 }597}598function commitLayoutEffectOnFiber(599 finishedRoot: FiberRoot,600 current: Fiber | null,601 finishedWork: Fiber,602 committedLanes: Lanes,603): void {604 if ((finishedWork.flags & LayoutMask) !== NoFlags) {605 switch (finishedWork.tag) {606 case FunctionComponent:607 case ForwardRef:608 case SimpleMemoComponent: {609 if (610 !enableSuspenseLayoutEffectSemantics ||611 !offscreenSubtreeWasHidden612 ) {613 // At this point layout effects have already been destroyed (during mutation phase).614 // This is done to prevent sibling component effects from interfering with each other,615 // e.g. a destroy function in one component should never override a ref set616 // by a create function in another component during the same commit.617 if (618 enableProfilerTimer &&619 enableProfilerCommitHooks &&620 finishedWork.mode & ProfileMode621 ) {622 try {623 startLayoutEffectTimer();624 commitHookEffectListMount(625 HookLayout | HookHasEffect,626 finishedWork,627 );628 } finally {629 recordLayoutEffectDuration(finishedWork);630 }631 } else {632 commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);633 }634 }635 break;636 }637 case ClassComponent: {638 const instance = finishedWork.stateNode;639 if (finishedWork.flags & Update) {640 if (!offscreenSubtreeWasHidden) {641 if (current === null) {642 // We could update instance props and state here,643 // but instead we rely on them being set during last render.644 // TODO: revisit this when we implement resuming.645 if (__DEV__) {646 if (647 finishedWork.type === finishedWork.elementType &&648 !didWarnAboutReassigningProps649 ) {650 if (instance.props !== finishedWork.memoizedProps) {651 console.error(652 'Expected %s props to match memoized props before ' +653 'componentDidMount. ' +654 'This might either be because of a bug in React, or because ' +655 'a component reassigns its own `this.props`. ' +656 'Please file an issue.',657 getComponentNameFromFiber(finishedWork) || 'instance',658 );659 }660 if (instance.state !== finishedWork.memoizedState) {661 console.error(662 'Expected %s state to match memoized state before ' +663 'componentDidMount. ' +664 'This might either be because of a bug in React, or because ' +665 'a component reassigns its own `this.state`. ' +666 'Please file an issue.',667 getComponentNameFromFiber(finishedWork) || 'instance',668 );669 }670 }671 }672 if (673 enableProfilerTimer &&674 enableProfilerCommitHooks &&675 finishedWork.mode & ProfileMode676 ) {677 try {678 startLayoutEffectTimer();679 instance.componentDidMount();680 } finally {681 recordLayoutEffectDuration(finishedWork);682 }683 } else {684 instance.componentDidMount();685 }686 } else {687 const prevProps =688 finishedWork.elementType === finishedWork.type689 ? current.memoizedProps690 : resolveDefaultProps(691 finishedWork.type,692 current.memoizedProps,693 );694 const prevState = current.memoizedState;695 // We could update instance props and state here,696 // but instead we rely on them being set during last render.697 // TODO: revisit this when we implement resuming.698 if (__DEV__) {699 if (700 finishedWork.type === finishedWork.elementType &&701 !didWarnAboutReassigningProps702 ) {703 if (instance.props !== finishedWork.memoizedProps) {704 console.error(705 'Expected %s props to match memoized props before ' +706 'componentDidUpdate. ' +707 'This might either be because of a bug in React, or because ' +708 'a component reassigns its own `this.props`. ' +709 'Please file an issue.',710 getComponentNameFromFiber(finishedWork) || 'instance',711 );712 }713 if (instance.state !== finishedWork.memoizedState) {714 console.error(715 'Expected %s state to match memoized state before ' +716 'componentDidUpdate. ' +717 'This might either be because of a bug in React, or because ' +718 'a component reassigns its own `this.state`. ' +719 'Please file an issue.',720 getComponentNameFromFiber(finishedWork) || 'instance',721 );722 }723 }724 }725 if (726 enableProfilerTimer &&727 enableProfilerCommitHooks &&728 finishedWork.mode & ProfileMode729 ) {730 try {731 startLayoutEffectTimer();732 instance.componentDidUpdate(733 prevProps,734 prevState,735 instance.__reactInternalSnapshotBeforeUpdate,736 );737 } finally {738 recordLayoutEffectDuration(finishedWork);739 }740 } else {741 instance.componentDidUpdate(742 prevProps,743 prevState,744 instance.__reactInternalSnapshotBeforeUpdate,745 );746 }747 }748 }749 }750 // TODO: I think this is now always non-null by the time it reaches the751 // commit phase. Consider removing the type check.752 const updateQueue: UpdateQueue<753 *,754 > | null = (finishedWork.updateQueue: any);755 if (updateQueue !== null) {756 if (__DEV__) {757 if (758 finishedWork.type === finishedWork.elementType &&759 !didWarnAboutReassigningProps760 ) {761 if (instance.props !== finishedWork.memoizedProps) {762 console.error(763 'Expected %s props to match memoized props before ' +764 'processing the update queue. ' +765 'This might either be because of a bug in React, or because ' +766 'a component reassigns its own `this.props`. ' +767 'Please file an issue.',768 getComponentNameFromFiber(finishedWork) || 'instance',769 );770 }771 if (instance.state !== finishedWork.memoizedState) {772 console.error(773 'Expected %s state to match memoized state before ' +774 'processing the update queue. ' +775 'This might either be because of a bug in React, or because ' +776 'a component reassigns its own `this.state`. ' +777 'Please file an issue.',778 getComponentNameFromFiber(finishedWork) || 'instance',779 );780 }781 }782 }783 // We could update instance props and state here,784 // but instead we rely on them being set during last render.785 // TODO: revisit this when we implement resuming.786 commitUpdateQueue(finishedWork, updateQueue, instance);787 }788 break;789 }790 case HostRoot: {791 // TODO: I think this is now always non-null by the time it reaches the792 // commit phase. Consider removing the type check.793 const updateQueue: UpdateQueue<794 *,795 > | null = (finishedWork.updateQueue: any);796 if (updateQueue !== null) {797 let instance = null;798 if (finishedWork.child !== null) {799 switch (finishedWork.child.tag) {800 case HostComponent:801 instance = getPublicInstance(finishedWork.child.stateNode);802 break;803 case ClassComponent:804 instance = finishedWork.child.stateNode;805 break;806 }807 }808 commitUpdateQueue(finishedWork, updateQueue, instance);809 }810 break;811 }812 case HostComponent: {813 const instance: Instance = finishedWork.stateNode;814 // Renderers may schedule work to be done after host components are mounted815 // (eg DOM renderer may schedule auto-focus for inputs and form controls).816 // These effects should only be committed when components are first mounted,817 // aka when there is no current/alternate.818 if (current === null && finishedWork.flags & Update) {819 const type = finishedWork.type;820 const props = finishedWork.memoizedProps;821 commitMount(instance, type, props, finishedWork);822 }823 break;824 }825 case HostText: {826 // We have no life-cycles associated with text.827 break;828 }829 case HostPortal: {830 // We have no life-cycles associated with portals.831 break;832 }833 case Profiler: {834 if (enableProfilerTimer) {835 const {onCommit, onRender} = finishedWork.memoizedProps;836 const {effectDuration} = finishedWork.stateNode;837 const commitTime = getCommitTime();838 let phase = current === null ? 'mount' : 'update';839 if (enableProfilerNestedUpdatePhase) {840 if (isCurrentUpdateNested()) {841 phase = 'nested-update';842 }843 }844 if (typeof onRender === 'function') {845 onRender(846 finishedWork.memoizedProps.id,847 phase,848 finishedWork.actualDuration,849 finishedWork.treeBaseDuration,850 finishedWork.actualStartTime,851 commitTime,852 );853 }854 if (enableProfilerCommitHooks) {855 if (typeof onCommit === 'function') {856 onCommit(857 finishedWork.memoizedProps.id,858 phase,859 effectDuration,860 commitTime,861 );862 }863 // Schedule a passive effect for this Profiler to call onPostCommit hooks.864 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,865 // because the effect is also where times bubble to parent Profilers.866 enqueuePendingPassiveProfilerEffect(finishedWork);867 // Propagate layout effect durations to the next nearest Profiler ancestor.868 // Do not reset these values until the next render so DevTools has a chance to read them first.869 let parentFiber = finishedWork.return;870 outer: while (parentFiber !== null) {871 switch (parentFiber.tag) {872 case HostRoot:873 const root = parentFiber.stateNode;874 root.effectDuration += effectDuration;875 break outer;876 case Profiler:877 const parentStateNode = parentFiber.stateNode;878 parentStateNode.effectDuration += effectDuration;879 break outer;880 }881 parentFiber = parentFiber.return;882 }883 }884 }885 break;886 }887 case SuspenseComponent: {888 commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);889 break;890 }891 case SuspenseListComponent:892 case IncompleteClassComponent:893 case ScopeComponent:894 case OffscreenComponent:895 case LegacyHiddenComponent:896 break;897 default:898 invariant(899 false,900 'This unit of work tag should not have side-effects. This error is ' +901 'likely caused by a bug in React. Please file an issue.',902 );903 }904 }905 if (!enableSuspenseLayoutEffectSemantics || !offscreenSubtreeWasHidden) {906 if (enableScopeAPI) {907 // TODO: This is a temporary solution that allowed us to transition away908 // from React Flare on www.909 if (finishedWork.flags & Ref && finishedWork.tag !== ScopeComponent) {910 commitAttachRef(finishedWork);911 }912 } else {913 if (finishedWork.flags & Ref) {914 commitAttachRef(finishedWork);915 }916 }917 }918}919function reappearLayoutEffectsOnFiber(node: Fiber) {920 // Turn on layout effects in a tree that previously disappeared.921 // TODO (Offscreen) Check: flags & LayoutStatic922 switch (node.tag) {923 case FunctionComponent:924 case ForwardRef:925 case SimpleMemoComponent: {926 if (927 enableProfilerTimer &&928 enableProfilerCommitHooks &&929 node.mode & ProfileMode930 ) {931 try {932 startLayoutEffectTimer();933 safelyCallCommitHookLayoutEffectListMount(node, node.return);934 } finally {935 recordLayoutEffectDuration(node);936 }937 } else {938 safelyCallCommitHookLayoutEffectListMount(node, node.return);939 }940 break;941 }942 case ClassComponent: {943 const instance = node.stateNode;944 if (typeof instance.componentDidMount === 'function') {945 safelyCallComponentDidMount(node, node.return, instance);946 }947 safelyAttachRef(node, node.return);948 break;949 }950 case HostComponent: {951 safelyAttachRef(node, node.return);952 break;953 }954 }955}956function hideOrUnhideAllChildren(finishedWork, isHidden) {957 // Only hide or unhide the top-most host nodes.958 let hostSubtreeRoot = null;959 if (supportsMutation) {960 // We only have the top Fiber that was inserted but we need to recurse down its961 // children to find all the terminal nodes.962 let node: Fiber = finishedWork;963 while (true) {964 if (node.tag === HostComponent) {965 if (hostSubtreeRoot === null) {966 hostSubtreeRoot = node;967 const instance = node.stateNode;968 if (isHidden) {969 hideInstance(instance);970 } else {971 unhideInstance(node.stateNode, node.memoizedProps);972 }973 }974 } else if (node.tag === HostText) {975 if (hostSubtreeRoot === null) {976 const instance = node.stateNode;977 if (isHidden) {978 hideTextInstance(instance);979 } else {980 unhideTextInstance(instance, node.memoizedProps);981 }982 }983 } else if (984 (node.tag === OffscreenComponent ||985 node.tag === LegacyHiddenComponent) &&986 (node.memoizedState: OffscreenState) !== null &&987 node !== finishedWork988 ) {989 // Found a nested Offscreen component that is hidden.990 // Don't search any deeper. This tree should remain hidden.991 } else if (node.child !== null) {992 node.child.return = node;993 node = node.child;994 continue;995 }996 if (node === finishedWork) {997 return;998 }999 while (node.sibling === null) {1000 if (node.return === null || node.return === finishedWork) {1001 return;1002 }1003 if (hostSubtreeRoot === node) {1004 hostSubtreeRoot = null;1005 }1006 node = node.return;1007 }1008 if (hostSubtreeRoot === node) {1009 hostSubtreeRoot = null;1010 }1011 node.sibling.return = node.return;1012 node = node.sibling;1013 }1014 }1015}1016function commitAttachRef(finishedWork: Fiber) {1017 const ref = finishedWork.ref;1018 if (ref !== null) {1019 const instance = finishedWork.stateNode;1020 let instanceToUse;1021 switch (finishedWork.tag) {1022 case HostComponent:1023 instanceToUse = getPublicInstance(instance);1024 break;1025 default:1026 instanceToUse = instance;1027 }1028 // Moved outside to ensure DCE works with this flag1029 if (enableScopeAPI && finishedWork.tag === ScopeComponent) {1030 instanceToUse = instance;1031 }1032 if (typeof ref === 'function') {1033 let retVal;1034 if (1035 enableProfilerTimer &&1036 enableProfilerCommitHooks &&1037 finishedWork.mode & ProfileMode1038 ) {1039 try {1040 startLayoutEffectTimer();1041 retVal = ref(instanceToUse);1042 } finally {1043 recordLayoutEffectDuration(finishedWork);1044 }1045 } else {1046 retVal = ref(instanceToUse);1047 }1048 if (__DEV__) {1049 if (1050 warnAboutCallbackRefReturningFunction &&1051 typeof retVal === 'function'1052 ) {1053 console.error(1054 'Unexpected return value from a callback ref in %s. ' +1055 'A callback ref should not return a function.',1056 getComponentNameFromFiber(finishedWork),1057 );1058 }1059 }1060 } else {1061 if (__DEV__) {1062 if (!ref.hasOwnProperty('current')) {1063 console.error(1064 'Unexpected ref object provided for %s. ' +1065 'Use either a ref-setter function or React.createRef().',1066 getComponentNameFromFiber(finishedWork),1067 );1068 }1069 }1070 ref.current = instanceToUse;1071 }1072 }1073}1074function commitDetachRef(current: Fiber) {1075 const currentRef = current.ref;1076 if (currentRef !== null) {1077 if (typeof currentRef === 'function') {1078 if (1079 enableProfilerTimer &&1080 enableProfilerCommitHooks &&1081 current.mode & ProfileMode1082 ) {1083 try {1084 startLayoutEffectTimer();1085 currentRef(null);1086 } finally {1087 recordLayoutEffectDuration(current);1088 }1089 } else {1090 currentRef(null);1091 }1092 } else {1093 currentRef.current = null;1094 }1095 }1096}1097// User-originating errors (lifecycles and refs) should not interrupt1098// deletion, so don't let them throw. Host-originating errors should1099// interrupt deletion, so it's okay1100function commitUnmount(1101 finishedRoot: FiberRoot,1102 current: Fiber,1103 nearestMountedAncestor: Fiber,1104): void {1105 onCommitUnmount(current);1106 switch (current.tag) {1107 case FunctionComponent:1108 case ForwardRef:1109 case MemoComponent:1110 case SimpleMemoComponent: {1111 const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);1112 if (updateQueue !== null) {1113 const lastEffect = updateQueue.lastEffect;1114 if (lastEffect !== null) {1115 const firstEffect = lastEffect.next;1116 let effect = firstEffect;1117 do {1118 const {destroy, tag} = effect;1119 if (destroy !== undefined) {1120 if (1121 (tag & HookInsertion) !== NoHookEffect ||1122 (tag & HookLayout) !== NoHookEffect1123 ) {1124 if (1125 enableProfilerTimer &&1126 enableProfilerCommitHooks &&1127 current.mode & ProfileMode1128 ) {1129 startLayoutEffectTimer();1130 safelyCallDestroy(current, nearestMountedAncestor, destroy);1131 recordLayoutEffectDuration(current);1132 } else {1133 safelyCallDestroy(current, nearestMountedAncestor, destroy);1134 }1135 }1136 }1137 effect = effect.next;1138 } while (effect !== firstEffect);1139 }1140 }1141 return;1142 }1143 case ClassComponent: {1144 safelyDetachRef(current, nearestMountedAncestor);1145 const instance = current.stateNode;1146 if (typeof instance.componentWillUnmount === 'function') {1147 safelyCallComponentWillUnmount(1148 current,1149 nearestMountedAncestor,1150 instance,1151 );1152 }1153 return;1154 }1155 case HostComponent: {1156 safelyDetachRef(current, nearestMountedAncestor);1157 return;1158 }1159 case HostPortal: {1160 // TODO: this is recursive.1161 // We are also not using this parent because1162 // the portal will get pushed immediately.1163 if (supportsMutation) {1164 unmountHostComponents(finishedRoot, current, nearestMountedAncestor);1165 } else if (supportsPersistence) {1166 emptyPortalContainer(current);1167 }1168 return;1169 }1170 case DehydratedFragment: {1171 if (enableSuspenseCallback) {1172 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1173 if (hydrationCallbacks !== null) {1174 const onDeleted = hydrationCallbacks.onDeleted;1175 if (onDeleted) {1176 onDeleted((current.stateNode: SuspenseInstance));1177 }1178 }1179 }1180 return;1181 }1182 case ScopeComponent: {1183 if (enableScopeAPI) {1184 safelyDetachRef(current, nearestMountedAncestor);1185 }1186 return;1187 }1188 }1189}1190function commitNestedUnmounts(1191 finishedRoot: FiberRoot,1192 root: Fiber,1193 nearestMountedAncestor: Fiber,1194): void {1195 // While we're inside a removed host node we don't want to call1196 // removeChild on the inner nodes because they're removed by the top1197 // call anyway. We also want to call componentWillUnmount on all1198 // composites before this host node is removed from the tree. Therefore1199 // we do an inner loop while we're still inside the host node.1200 let node: Fiber = root;1201 while (true) {1202 commitUnmount(finishedRoot, node, nearestMountedAncestor);1203 // Visit children because they may contain more composite or host nodes.1204 // Skip portals because commitUnmount() currently visits them recursively.1205 if (1206 node.child !== null &&1207 // If we use mutation we drill down into portals using commitUnmount above.1208 // If we don't use mutation we drill down into portals here instead.1209 (!supportsMutation || node.tag !== HostPortal)1210 ) {1211 node.child.return = node;1212 node = node.child;1213 continue;1214 }1215 if (node === root) {1216 return;1217 }1218 while (node.sibling === null) {1219 if (node.return === null || node.return === root) {1220 return;1221 }1222 node = node.return;1223 }1224 node.sibling.return = node.return;1225 node = node.sibling;1226 }1227}1228function detachFiberMutation(fiber: Fiber) {1229 // Cut off the return pointer to disconnect it from the tree.1230 // This enables us to detect and warn against state updates on an unmounted component.1231 // It also prevents events from bubbling from within disconnected components.1232 //1233 // Ideally, we should also clear the child pointer of the parent alternate to let this1234 // get GC:ed but we don't know which for sure which parent is the current1235 // one so we'll settle for GC:ing the subtree of this child.1236 // This child itself will be GC:ed when the parent updates the next time.1237 //1238 // Note that we can't clear child or sibling pointers yet.1239 // They're needed for passive effects and for findDOMNode.1240 // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).1241 //1242 // Don't reset the alternate yet, either. We need that so we can detach the1243 // alternate's fields in the passive phase. Clearing the return pointer is1244 // sufficient for findDOMNode semantics.1245 const alternate = fiber.alternate;1246 if (alternate !== null) {1247 alternate.return = null;1248 }1249 fiber.return = null;1250}1251function detachFiberAfterEffects(fiber: Fiber) {1252 const alternate = fiber.alternate;1253 if (alternate !== null) {1254 fiber.alternate = null;1255 detachFiberAfterEffects(alternate);1256 }1257 // Note: Defensively using negation instead of < in case1258 // `deletedTreeCleanUpLevel` is undefined.1259 if (!(deletedTreeCleanUpLevel >= 2)) {1260 // This is the default branch (level 0).1261 fiber.child = null;1262 fiber.deletions = null;1263 fiber.dependencies = null;1264 fiber.memoizedProps = null;1265 fiber.memoizedState = null;1266 fiber.pendingProps = null;1267 fiber.sibling = null;1268 fiber.stateNode = null;1269 fiber.updateQueue = null;1270 if (__DEV__) {1271 fiber._debugOwner = null;1272 }1273 } else {1274 // Clear cyclical Fiber fields. This level alone is designed to roughly1275 // approximate the planned Fiber refactor. In that world, `setState` will be1276 // bound to a special "instance" object instead of a Fiber. The Instance1277 // object will not have any of these fields. It will only be connected to1278 // the fiber tree via a single link at the root. So if this level alone is1279 // sufficient to fix memory issues, that bodes well for our plans.1280 fiber.child = null;1281 fiber.deletions = null;1282 fiber.sibling = null;1283 // The `stateNode` is cyclical because on host nodes it points to the host1284 // tree, which has its own pointers to children, parents, and siblings.1285 // The other host nodes also point back to fibers, so we should detach that1286 // one, too.1287 if (fiber.tag === HostComponent) {1288 const hostInstance: Instance = fiber.stateNode;1289 if (hostInstance !== null) {1290 detachDeletedInstance(hostInstance);1291 }1292 }1293 fiber.stateNode = null;1294 // I'm intentionally not clearing the `return` field in this level. We1295 // already disconnect the `return` pointer at the root of the deleted1296 // subtree (in `detachFiberMutation`). Besides, `return` by itself is not1297 // cyclical â it's only cyclical when combined with `child`, `sibling`, and1298 // `alternate`. But we'll clear it in the next level anyway, just in case.1299 if (__DEV__) {1300 fiber._debugOwner = null;1301 }1302 if (deletedTreeCleanUpLevel >= 3) {1303 // Theoretically, nothing in here should be necessary, because we already1304 // disconnected the fiber from the tree. So even if something leaks this1305 // particular fiber, it won't leak anything else1306 //1307 // The purpose of this branch is to be super aggressive so we can measure1308 // if there's any difference in memory impact. If there is, that could1309 // indicate a React leak we don't know about.1310 fiber.return = null;1311 fiber.dependencies = null;1312 fiber.memoizedProps = null;1313 fiber.memoizedState = null;1314 fiber.pendingProps = null;1315 fiber.stateNode = null;1316 // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.1317 fiber.updateQueue = null;1318 }1319 }1320}1321function emptyPortalContainer(current: Fiber) {1322 if (!supportsPersistence) {1323 return;1324 }1325 const portal: {1326 containerInfo: Container,1327 pendingChildren: ChildSet,1328 ...1329 } = current.stateNode;1330 const {containerInfo} = portal;1331 const emptyChildSet = createContainerChildSet(containerInfo);1332 replaceContainerChildren(containerInfo, emptyChildSet);1333}1334function commitContainer(finishedWork: Fiber) {1335 if (!supportsPersistence) {1336 return;1337 }1338 switch (finishedWork.tag) {1339 case ClassComponent:1340 case HostComponent:1341 case HostText: {1342 return;1343 }1344 case HostRoot:1345 case HostPortal: {1346 const portalOrRoot: {1347 containerInfo: Container,1348 pendingChildren: ChildSet,1349 ...1350 } = finishedWork.stateNode;1351 const {containerInfo, pendingChildren} = portalOrRoot;1352 replaceContainerChildren(containerInfo, pendingChildren);1353 return;1354 }1355 }1356 invariant(1357 false,1358 'This unit of work tag should not have side-effects. This error is ' +1359 'likely caused by a bug in React. Please file an issue.',1360 );1361}1362function getHostParentFiber(fiber: Fiber): Fiber {1363 let parent = fiber.return;1364 while (parent !== null) {1365 if (isHostParent(parent)) {1366 return parent;1367 }1368 parent = parent.return;1369 }1370 invariant(1371 false,1372 'Expected to find a host parent. This error is likely caused by a bug ' +1373 'in React. Please file an issue.',1374 );1375}1376function isHostParent(fiber: Fiber): boolean {1377 return (1378 fiber.tag === HostComponent ||1379 fiber.tag === HostRoot ||1380 fiber.tag === HostPortal1381 );1382}1383function getHostSibling(fiber: Fiber): ?Instance {1384 // We're going to search forward into the tree until we find a sibling host1385 // node. Unfortunately, if multiple insertions are done in a row we have to1386 // search past them. This leads to exponential search for the next sibling.1387 // TODO: Find a more efficient way to do this.1388 let node: Fiber = fiber;1389 siblings: while (true) {1390 // If we didn't find anything, let's try the next sibling.1391 while (node.sibling === null) {1392 if (node.return === null || isHostParent(node.return)) {1393 // If we pop out of the root or hit the parent the fiber we are the1394 // last sibling.1395 return null;1396 }1397 node = node.return;1398 }1399 node.sibling.return = node.return;1400 node = node.sibling;1401 while (1402 node.tag !== HostComponent &&1403 node.tag !== HostText &&1404 node.tag !== DehydratedFragment1405 ) {1406 // If it is not host node and, we might have a host node inside it.1407 // Try to search down until we find one.1408 if (node.flags & Placement) {1409 // If we don't have a child, try the siblings instead.1410 continue siblings;1411 }1412 // If we don't have a child, try the siblings instead.1413 // We also skip portals because they are not part of this host tree.1414 if (node.child === null || node.tag === HostPortal) {1415 continue siblings;1416 } else {1417 node.child.return = node;1418 node = node.child;1419 }1420 }1421 // Check if this host node is stable or about to be placed.1422 if (!(node.flags & Placement)) {1423 // Found it!1424 return node.stateNode;1425 }1426 }1427}1428function commitPlacement(finishedWork: Fiber): void {1429 if (!supportsMutation) {1430 return;1431 }1432 // Recursively insert all host nodes into the parent.1433 const parentFiber = getHostParentFiber(finishedWork);1434 // Note: these two variables *must* always be updated together.1435 let parent;1436 let isContainer;1437 const parentStateNode = parentFiber.stateNode;1438 switch (parentFiber.tag) {1439 case HostComponent:1440 parent = parentStateNode;1441 isContainer = false;1442 break;1443 case HostRoot:1444 parent = parentStateNode.containerInfo;1445 isContainer = true;1446 break;1447 case HostPortal:1448 parent = parentStateNode.containerInfo;1449 isContainer = true;1450 break;1451 // eslint-disable-next-line-no-fallthrough1452 default:1453 invariant(1454 false,1455 'Invalid host parent fiber. This error is likely caused by a bug ' +1456 'in React. Please file an issue.',1457 );1458 }1459 if (parentFiber.flags & ContentReset) {1460 // Reset the text content of the parent before doing any insertions1461 resetTextContent(parent);1462 // Clear ContentReset from the effect tag1463 parentFiber.flags &= ~ContentReset;1464 }1465 const before = getHostSibling(finishedWork);1466 // We only have the top Fiber that was inserted but we need to recurse down its1467 // children to find all the terminal nodes.1468 if (isContainer) {1469 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);1470 } else {1471 insertOrAppendPlacementNode(finishedWork, before, parent);1472 }1473}1474function insertOrAppendPlacementNodeIntoContainer(1475 node: Fiber,1476 before: ?Instance,1477 parent: Container,1478): void {1479 const {tag} = node;1480 const isHost = tag === HostComponent || tag === HostText;1481 if (isHost) {1482 const stateNode = node.stateNode;1483 if (before) {1484 insertInContainerBefore(parent, stateNode, before);1485 } else {1486 appendChildToContainer(parent, stateNode);1487 }1488 } else if (tag === HostPortal) {1489 // If the insertion itself is a portal, then we don't want to traverse1490 // down its children. Instead, we'll get insertions from each child in1491 // the portal directly.1492 } else {1493 const child = node.child;1494 if (child !== null) {1495 insertOrAppendPlacementNodeIntoContainer(child, before, parent);1496 let sibling = child.sibling;1497 while (sibling !== null) {1498 insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);1499 sibling = sibling.sibling;1500 }1501 }1502 }1503}1504function insertOrAppendPlacementNode(1505 node: Fiber,1506 before: ?Instance,1507 parent: Instance,1508): void {1509 const {tag} = node;1510 const isHost = tag === HostComponent || tag === HostText;1511 if (isHost) {1512 const stateNode = node.stateNode;1513 if (before) {1514 insertBefore(parent, stateNode, before);1515 } else {1516 appendChild(parent, stateNode);1517 }1518 } else if (tag === HostPortal) {1519 // If the insertion itself is a portal, then we don't want to traverse1520 // down its children. Instead, we'll get insertions from each child in1521 // the portal directly.1522 } else {1523 const child = node.child;1524 if (child !== null) {1525 insertOrAppendPlacementNode(child, before, parent);1526 let sibling = child.sibling;1527 while (sibling !== null) {1528 insertOrAppendPlacementNode(sibling, before, parent);1529 sibling = sibling.sibling;1530 }1531 }1532 }1533}1534function unmountHostComponents(1535 finishedRoot: FiberRoot,1536 current: Fiber,1537 nearestMountedAncestor: Fiber,1538): void {1539 // We only have the top Fiber that was deleted but we need to recurse down its1540 // children to find all the terminal nodes.1541 let node: Fiber = current;1542 // Each iteration, currentParent is populated with node's host parent if not1543 // currentParentIsValid.1544 let currentParentIsValid = false;1545 // Note: these two variables *must* always be updated together.1546 let currentParent;1547 let currentParentIsContainer;1548 while (true) {1549 if (!currentParentIsValid) {1550 let parent = node.return;1551 findParent: while (true) {1552 invariant(1553 parent !== null,1554 'Expected to find a host parent. This error is likely caused by ' +1555 'a bug in React. Please file an issue.',1556 );1557 const parentStateNode = parent.stateNode;1558 switch (parent.tag) {1559 case HostComponent:1560 currentParent = parentStateNode;1561 currentParentIsContainer = false;1562 break findParent;1563 case HostRoot:1564 currentParent = parentStateNode.containerInfo;1565 currentParentIsContainer = true;1566 break findParent;1567 case HostPortal:1568 currentParent = parentStateNode.containerInfo;1569 currentParentIsContainer = true;1570 break findParent;1571 }1572 parent = parent.return;1573 }1574 currentParentIsValid = true;1575 }1576 if (node.tag === HostComponent || node.tag === HostText) {1577 commitNestedUnmounts(finishedRoot, node, nearestMountedAncestor);1578 // After all the children have unmounted, it is now safe to remove the1579 // node from the tree.1580 if (currentParentIsContainer) {1581 removeChildFromContainer(1582 ((currentParent: any): Container),1583 (node.stateNode: Instance | TextInstance),1584 );1585 } else {1586 removeChild(1587 ((currentParent: any): Instance),1588 (node.stateNode: Instance | TextInstance),1589 );1590 }1591 // Don't visit children because we already visited them.1592 } else if (1593 enableSuspenseServerRenderer &&1594 node.tag === DehydratedFragment1595 ) {1596 if (enableSuspenseCallback) {1597 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1598 if (hydrationCallbacks !== null) {1599 const onDeleted = hydrationCallbacks.onDeleted;1600 if (onDeleted) {1601 onDeleted((node.stateNode: SuspenseInstance));1602 }1603 }1604 }1605 // Delete the dehydrated suspense boundary and all of its content.1606 if (currentParentIsContainer) {1607 clearSuspenseBoundaryFromContainer(1608 ((currentParent: any): Container),1609 (node.stateNode: SuspenseInstance),1610 );1611 } else {1612 clearSuspenseBoundary(1613 ((currentParent: any): Instance),1614 (node.stateNode: SuspenseInstance),1615 );1616 }1617 } else if (node.tag === HostPortal) {1618 if (node.child !== null) {1619 // When we go into a portal, it becomes the parent to remove from.1620 // We will reassign it back when we pop the portal on the way up.1621 currentParent = node.stateNode.containerInfo;1622 currentParentIsContainer = true;1623 // Visit children because portals might contain host components.1624 node.child.return = node;1625 node = node.child;1626 continue;1627 }1628 } else {1629 commitUnmount(finishedRoot, node, nearestMountedAncestor);1630 // Visit children because we may find more host components below.1631 if (node.child !== null) {1632 node.child.return = node;1633 node = node.child;1634 continue;1635 }1636 }1637 if (node === current) {1638 return;1639 }1640 while (node.sibling === null) {1641 if (node.return === null || node.return === current) {1642 return;1643 }1644 node = node.return;1645 if (node.tag === HostPortal) {1646 // When we go out of the portal, we need to restore the parent.1647 // Since we don't keep a stack of them, we will search for it.1648 currentParentIsValid = false;1649 }1650 }1651 node.sibling.return = node.return;1652 node = node.sibling;1653 }1654}1655function commitDeletion(1656 finishedRoot: FiberRoot,1657 current: Fiber,1658 nearestMountedAncestor: Fiber,1659): void {1660 if (supportsMutation) {1661 // Recursively delete all host nodes from the parent.1662 // Detach refs and call componentWillUnmount() on the whole subtree.1663 unmountHostComponents(finishedRoot, current, nearestMountedAncestor);1664 } else {1665 // Detach refs and call componentWillUnmount() on the whole subtree.1666 commitNestedUnmounts(finishedRoot, current, nearestMountedAncestor);1667 }1668 detachFiberMutation(current);1669}1670function commitWork(current: Fiber | null, finishedWork: Fiber): void {1671 if (!supportsMutation) {1672 switch (finishedWork.tag) {1673 case FunctionComponent:1674 case ForwardRef:1675 case MemoComponent:1676 case SimpleMemoComponent: {1677 commitHookEffectListUnmount(1678 HookInsertion | HookHasEffect,1679 finishedWork,1680 finishedWork.return,1681 );1682 commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);1683 // Layout effects are destroyed during the mutation phase so that all1684 // destroy functions for all fibers are called before any create functions.1685 // This prevents sibling component effects from interfering with each other,1686 // e.g. a destroy function in one component should never override a ref set1687 // by a create function in another component during the same commit.1688 // TODO: Check if we're inside an Offscreen subtree that disappeared1689 // during this commit. If so, we would have already unmounted its1690 // layout hooks. (However, since we null out the `destroy` function1691 // right before calling it, the behavior is already correct, so this1692 // would mostly be for modeling purposes.)1693 if (1694 enableProfilerTimer &&1695 enableProfilerCommitHooks &&1696 finishedWork.mode & ProfileMode1697 ) {1698 try {1699 startLayoutEffectTimer();1700 commitHookEffectListUnmount(1701 HookLayout | HookHasEffect,1702 finishedWork,1703 finishedWork.return,1704 );1705 } finally {1706 recordLayoutEffectDuration(finishedWork);1707 }1708 } else {1709 commitHookEffectListUnmount(1710 HookLayout | HookHasEffect,1711 finishedWork,1712 finishedWork.return,1713 );1714 }1715 return;1716 }1717 case Profiler: {1718 return;1719 }1720 case SuspenseComponent: {1721 commitSuspenseCallback(finishedWork);1722 attachSuspenseRetryListeners(finishedWork);1723 return;1724 }1725 case SuspenseListComponent: {1726 attachSuspenseRetryListeners(finishedWork);1727 return;1728 }1729 case HostRoot: {1730 if (supportsHydration) {1731 const root: FiberRoot = finishedWork.stateNode;1732 if (root.isDehydrated) {1733 // We've just hydrated. No need to hydrate again.1734 root.isDehydrated = false;1735 commitHydratedContainer(root.containerInfo);1736 }1737 }1738 break;1739 }1740 case OffscreenComponent:1741 case LegacyHiddenComponent: {1742 return;1743 }1744 }1745 commitContainer(finishedWork);1746 return;1747 }1748 switch (finishedWork.tag) {1749 case FunctionComponent:1750 case ForwardRef:1751 case MemoComponent:1752 case SimpleMemoComponent: {1753 commitHookEffectListUnmount(1754 HookInsertion | HookHasEffect,1755 finishedWork,1756 finishedWork.return,1757 );1758 commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);1759 // Layout effects are destroyed during the mutation phase so that all1760 // destroy functions for all fibers are called before any create functions.1761 // This prevents sibling component effects from interfering with each other,1762 // e.g. a destroy function in one component should never override a ref set1763 // by a create function in another component during the same commit.1764 if (1765 enableProfilerTimer &&1766 enableProfilerCommitHooks &&1767 finishedWork.mode & ProfileMode1768 ) {1769 try {1770 startLayoutEffectTimer();1771 commitHookEffectListUnmount(1772 HookLayout | HookHasEffect,1773 finishedWork,1774 finishedWork.return,1775 );1776 } finally {1777 recordLayoutEffectDuration(finishedWork);1778 }1779 } else {1780 commitHookEffectListUnmount(1781 HookLayout | HookHasEffect,1782 finishedWork,1783 finishedWork.return,1784 );1785 }1786 return;1787 }1788 case ClassComponent: {1789 return;1790 }1791 case HostComponent: {1792 const instance: Instance = finishedWork.stateNode;1793 if (instance != null) {1794 // Commit the work prepared earlier.1795 const newProps = finishedWork.memoizedProps;1796 // For hydration we reuse the update path but we treat the oldProps1797 // as the newProps. The updatePayload will contain the real change in1798 // this case.1799 const oldProps = current !== null ? current.memoizedProps : newProps;1800 const type = finishedWork.type;1801 // TODO: Type the updateQueue to be specific to host components.1802 const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);1803 finishedWork.updateQueue = null;1804 if (updatePayload !== null) {1805 commitUpdate(1806 instance,1807 updatePayload,1808 type,1809 oldProps,1810 newProps,1811 finishedWork,1812 );1813 }1814 }1815 return;1816 }1817 case HostText: {1818 invariant(1819 finishedWork.stateNode !== null,1820 'This should have a text node initialized. This error is likely ' +1821 'caused by a bug in React. Please file an issue.',1822 );1823 const textInstance: TextInstance = finishedWork.stateNode;1824 const newText: string = finishedWork.memoizedProps;1825 // For hydration we reuse the update path but we treat the oldProps1826 // as the newProps. The updatePayload will contain the real change in1827 // this case.1828 const oldText: string =1829 current !== null ? current.memoizedProps : newText;1830 commitTextUpdate(textInstance, oldText, newText);1831 return;1832 }1833 case HostRoot: {1834 if (supportsHydration) {1835 const root: FiberRoot = finishedWork.stateNode;1836 if (root.isDehydrated) {1837 // We've just hydrated. No need to hydrate again.1838 root.isDehydrated = false;1839 commitHydratedContainer(root.containerInfo);1840 }1841 }1842 return;1843 }1844 case Profiler: {1845 return;1846 }1847 case SuspenseComponent: {1848 commitSuspenseCallback(finishedWork);1849 attachSuspenseRetryListeners(finishedWork);1850 return;1851 }1852 case SuspenseListComponent: {1853 attachSuspenseRetryListeners(finishedWork);1854 return;1855 }1856 case IncompleteClassComponent: {1857 return;1858 }1859 case ScopeComponent: {1860 if (enableScopeAPI) {1861 const scopeInstance = finishedWork.stateNode;1862 prepareScopeUpdate(scopeInstance, finishedWork);1863 return;1864 }1865 break;1866 }1867 }1868 invariant(1869 false,1870 'This unit of work tag should not have side-effects. This error is ' +1871 'likely caused by a bug in React. Please file an issue.',1872 );1873}1874function commitSuspenseCallback(finishedWork: Fiber) {1875 // TODO: Move this to passive phase1876 const newState: SuspenseState | null = finishedWork.memoizedState;1877 if (enableSuspenseCallback && newState !== null) {1878 const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;1879 if (typeof suspenseCallback === 'function') {1880 const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);1881 if (wakeables !== null) {1882 suspenseCallback(new Set(wakeables));1883 }1884 } else if (__DEV__) {1885 if (suspenseCallback !== undefined) {1886 console.error('Unexpected type for suspenseCallback.');1887 }1888 }1889 }1890}1891function commitSuspenseHydrationCallbacks(1892 finishedRoot: FiberRoot,1893 finishedWork: Fiber,1894) {1895 if (!supportsHydration) {1896 return;1897 }1898 const newState: SuspenseState | null = finishedWork.memoizedState;1899 if (newState === null) {1900 const current = finishedWork.alternate;1901 if (current !== null) {1902 const prevState: SuspenseState | null = current.memoizedState;1903 if (prevState !== null) {1904 const suspenseInstance = prevState.dehydrated;1905 if (suspenseInstance !== null) {1906 commitHydratedSuspenseInstance(suspenseInstance);1907 if (enableSuspenseCallback) {1908 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1909 if (hydrationCallbacks !== null) {1910 const onHydrated = hydrationCallbacks.onHydrated;1911 if (onHydrated) {1912 onHydrated(suspenseInstance);1913 }1914 }1915 }1916 }1917 }1918 }1919 }1920}1921function attachSuspenseRetryListeners(finishedWork: Fiber) {1922 // If this boundary just timed out, then it will have a set of wakeables.1923 // For each wakeable, attach a listener so that when it resolves, React1924 // attempts to re-render the boundary in the primary (pre-timeout) state.1925 const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);1926 if (wakeables !== null) {1927 finishedWork.updateQueue = null;1928 let retryCache = finishedWork.stateNode;1929 if (retryCache === null) {1930 retryCache = finishedWork.stateNode = new PossiblyWeakSet();1931 }1932 wakeables.forEach(wakeable => {1933 // Memoize using the boundary fiber to prevent redundant listeners.1934 const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);1935 if (!retryCache.has(wakeable)) {1936 retryCache.add(wakeable);1937 if (enableUpdaterTracking) {1938 if (isDevToolsPresent) {1939 if (inProgressLanes !== null && inProgressRoot !== null) {1940 // If we have pending work still, associate the original updaters with it.1941 restorePendingUpdaters(inProgressRoot, inProgressLanes);1942 } else {1943 throw Error(1944 'Expected finished root and lanes to be set. This is a bug in React.',1945 );1946 }1947 }1948 }1949 wakeable.then(retry, retry);1950 }1951 });1952 }1953}1954// This function detects when a Suspense boundary goes from visible to hidden.1955// It returns false if the boundary is already hidden.1956// TODO: Use an effect tag.1957export function isSuspenseBoundaryBeingHidden(1958 current: Fiber | null,1959 finishedWork: Fiber,1960): boolean {1961 if (current !== null) {1962 const oldState: SuspenseState | null = current.memoizedState;1963 if (oldState === null || oldState.dehydrated !== null) {1964 const newState: SuspenseState | null = finishedWork.memoizedState;1965 return newState !== null && newState.dehydrated === null;1966 }1967 }1968 return false;1969}1970function commitResetTextContent(current: Fiber) {1971 if (!supportsMutation) {1972 return;1973 }1974 resetTextContent(current.stateNode);1975}1976export function commitMutationEffects(1977 root: FiberRoot,1978 firstChild: Fiber,1979 committedLanes: Lanes,1980) {1981 inProgressLanes = committedLanes;1982 inProgressRoot = root;1983 nextEffect = firstChild;1984 commitMutationEffects_begin(root);1985 inProgressLanes = null;1986 inProgressRoot = null;1987}1988function commitMutationEffects_begin(root: FiberRoot) {1989 while (nextEffect !== null) {1990 const fiber = nextEffect;1991 // TODO: Should wrap this in flags check, too, as optimization1992 const deletions = fiber.deletions;1993 if (deletions !== null) {1994 for (let i = 0; i < deletions.length; i++) {1995 const childToDelete = deletions[i];1996 try {1997 commitDeletion(root, childToDelete, fiber);1998 } catch (error) {1999 reportUncaughtErrorInDEV(error);2000 captureCommitPhaseError(childToDelete, fiber, error);2001 }2002 }2003 }2004 const child = fiber.child;2005 if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {2006 ensureCorrectReturnPointer(child, fiber);2007 nextEffect = child;2008 } else {2009 commitMutationEffects_complete(root);2010 }2011 }2012}2013function commitMutationEffects_complete(root: FiberRoot) {2014 while (nextEffect !== null) {2015 const fiber = nextEffect;2016 setCurrentDebugFiberInDEV(fiber);2017 try {2018 commitMutationEffectsOnFiber(fiber, root);2019 } catch (error) {2020 reportUncaughtErrorInDEV(error);2021 captureCommitPhaseError(fiber, fiber.return, error);2022 }2023 resetCurrentDebugFiberInDEV();2024 const sibling = fiber.sibling;2025 if (sibling !== null) {2026 ensureCorrectReturnPointer(sibling, fiber.return);2027 nextEffect = sibling;2028 return;2029 }2030 nextEffect = fiber.return;2031 }2032}2033function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {2034 // TODO: The factoring of this phase could probably be improved. Consider2035 // switching on the type of work before checking the flags. That's what2036 // we do in all the other phases. I think this one is only different2037 // because of the shared reconciliation logic below.2038 const flags = finishedWork.flags;2039 if (flags & ContentReset) {2040 commitResetTextContent(finishedWork);2041 }2042 if (flags & Ref) {2043 const current = finishedWork.alternate;2044 if (current !== null) {2045 commitDetachRef(current);2046 }2047 if (enableScopeAPI) {2048 // TODO: This is a temporary solution that allowed us to transition away2049 // from React Flare on www.2050 if (finishedWork.tag === ScopeComponent) {2051 commitAttachRef(finishedWork);2052 }2053 }2054 }2055 if (flags & Visibility) {2056 switch (finishedWork.tag) {2057 case SuspenseComponent: {2058 const newState: OffscreenState | null = finishedWork.memoizedState;2059 const isHidden = newState !== null;2060 if (isHidden) {2061 const current = finishedWork.alternate;2062 const wasHidden = current !== null && current.memoizedState !== null;2063 if (!wasHidden) {2064 // TODO: Move to passive phase2065 markCommitTimeOfFallback();2066 }2067 }2068 break;2069 }2070 case OffscreenComponent: {2071 const newState: OffscreenState | null = finishedWork.memoizedState;2072 const isHidden = newState !== null;2073 const current = finishedWork.alternate;2074 const wasHidden = current !== null && current.memoizedState !== null;2075 const offscreenBoundary: Fiber = finishedWork;2076 if (supportsMutation) {2077 // TODO: This needs to run whenever there's an insertion or update2078 // inside a hidden Offscreen tree.2079 hideOrUnhideAllChildren(offscreenBoundary, isHidden);2080 }2081 if (enableSuspenseLayoutEffectSemantics) {2082 if (isHidden) {2083 if (!wasHidden) {2084 if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {2085 nextEffect = offscreenBoundary;2086 let offscreenChild = offscreenBoundary.child;2087 while (offscreenChild !== null) {2088 nextEffect = offscreenChild;2089 disappearLayoutEffects_begin(offscreenChild);2090 offscreenChild = offscreenChild.sibling;2091 }2092 }2093 }2094 } else {2095 if (wasHidden) {2096 // TODO: Move re-appear call here for symmetry?2097 }2098 }2099 break;2100 }2101 }2102 }2103 }2104 // The following switch statement is only concerned about placement,2105 // updates, and deletions. To avoid needing to add a case for every possible2106 // bitmap value, we remove the secondary effects from the effect tag and2107 // switch on that value.2108 const primaryFlags = flags & (Placement | Update | Hydrating);2109 outer: switch (primaryFlags) {2110 case Placement: {2111 commitPlacement(finishedWork);2112 // Clear the "placement" from effect tag so that we know that this is2113 // inserted, before any life-cycles like componentDidMount gets called.2114 // TODO: findDOMNode doesn't rely on this any more but isMounted does2115 // and isMounted is deprecated anyway so we should be able to kill this.2116 finishedWork.flags &= ~Placement;2117 break;2118 }2119 case PlacementAndUpdate: {2120 // Placement2121 commitPlacement(finishedWork);2122 // Clear the "placement" from effect tag so that we know that this is2123 // inserted, before any life-cycles like componentDidMount gets called.2124 finishedWork.flags &= ~Placement;2125 // Update2126 const current = finishedWork.alternate;2127 commitWork(current, finishedWork);2128 break;2129 }2130 case Hydrating: {2131 finishedWork.flags &= ~Hydrating;2132 break;2133 }2134 case HydratingAndUpdate: {2135 finishedWork.flags &= ~Hydrating;2136 // Update2137 const current = finishedWork.alternate;2138 commitWork(current, finishedWork);2139 break;2140 }2141 case Update: {2142 const current = finishedWork.alternate;2143 commitWork(current, finishedWork);2144 break;2145 }2146 }2147}2148export function commitLayoutEffects(2149 finishedWork: Fiber,2150 root: FiberRoot,2151 committedLanes: Lanes,2152): void {2153 inProgressLanes = committedLanes;2154 inProgressRoot = root;2155 nextEffect = finishedWork;2156 commitLayoutEffects_begin(finishedWork, root, committedLanes);2157 inProgressLanes = null;2158 inProgressRoot = null;2159}2160function commitLayoutEffects_begin(2161 subtreeRoot: Fiber,2162 root: FiberRoot,2163 committedLanes: Lanes,2164) {2165 // Suspense layout effects semantics don't change for legacy roots.2166 const isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;2167 while (nextEffect !== null) {2168 const fiber = nextEffect;2169 const firstChild = fiber.child;2170 if (2171 enableSuspenseLayoutEffectSemantics &&2172 fiber.tag === OffscreenComponent &&2173 isModernRoot2174 ) {2175 // Keep track of the current Offscreen stack's state.2176 const isHidden = fiber.memoizedState !== null;2177 const newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;2178 if (newOffscreenSubtreeIsHidden) {2179 // The Offscreen tree is hidden. Skip over its layout effects.2180 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);2181 continue;2182 } else {2183 // TODO (Offscreen) Also check: subtreeFlags & LayoutMask2184 const current = fiber.alternate;2185 const wasHidden = current !== null && current.memoizedState !== null;2186 const newOffscreenSubtreeWasHidden =2187 wasHidden || offscreenSubtreeWasHidden;2188 const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;2189 const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;2190 // Traverse the Offscreen subtree with the current Offscreen as the root.2191 offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;2192 offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;2193 if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {2194 // This is the root of a reappearing boundary. Turn its layout effects2195 // back on.2196 nextEffect = fiber;2197 reappearLayoutEffects_begin(fiber);2198 }2199 let child = firstChild;2200 while (child !== null) {2201 nextEffect = child;2202 commitLayoutEffects_begin(2203 child, // New root; bubble back up to here and stop.2204 root,2205 committedLanes,2206 );2207 child = child.sibling;2208 }2209 // Restore Offscreen state and resume in our-progress traversal.2210 nextEffect = fiber;2211 offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;2212 offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;2213 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);2214 continue;2215 }2216 }2217 if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null) {2218 ensureCorrectReturnPointer(firstChild, fiber);2219 nextEffect = firstChild;2220 } else {2221 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);2222 }2223 }2224}2225function commitLayoutMountEffects_complete(2226 subtreeRoot: Fiber,2227 root: FiberRoot,2228 committedLanes: Lanes,2229) {2230 while (nextEffect !== null) {2231 const fiber = nextEffect;2232 if ((fiber.flags & LayoutMask) !== NoFlags) {2233 const current = fiber.alternate;2234 setCurrentDebugFiberInDEV(fiber);2235 try {2236 commitLayoutEffectOnFiber(root, current, fiber, committedLanes);2237 } catch (error) {2238 reportUncaughtErrorInDEV(error);2239 captureCommitPhaseError(fiber, fiber.return, error);2240 }2241 resetCurrentDebugFiberInDEV();2242 }2243 if (fiber === subtreeRoot) {2244 nextEffect = null;2245 return;2246 }2247 const sibling = fiber.sibling;2248 if (sibling !== null) {2249 ensureCorrectReturnPointer(sibling, fiber.return);2250 nextEffect = sibling;2251 return;2252 }2253 nextEffect = fiber.return;2254 }2255}2256function disappearLayoutEffects_begin(subtreeRoot: Fiber) {2257 while (nextEffect !== null) {2258 const fiber = nextEffect;2259 const firstChild = fiber.child;2260 // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)2261 switch (fiber.tag) {2262 case FunctionComponent:2263 case ForwardRef:2264 case MemoComponent:2265 case SimpleMemoComponent: {2266 if (2267 enableProfilerTimer &&2268 enableProfilerCommitHooks &&2269 fiber.mode & ProfileMode2270 ) {2271 try {2272 startLayoutEffectTimer();2273 commitHookEffectListUnmount(HookLayout, fiber, fiber.return);2274 } finally {2275 recordLayoutEffectDuration(fiber);2276 }2277 } else {2278 commitHookEffectListUnmount(HookLayout, fiber, fiber.return);2279 }2280 break;2281 }2282 case ClassComponent: {2283 // TODO (Offscreen) Check: flags & RefStatic2284 safelyDetachRef(fiber, fiber.return);2285 const instance = fiber.stateNode;2286 if (typeof instance.componentWillUnmount === 'function') {2287 safelyCallComponentWillUnmount(fiber, fiber.return, instance);2288 }2289 break;2290 }2291 case HostComponent: {2292 safelyDetachRef(fiber, fiber.return);2293 break;2294 }2295 case OffscreenComponent: {2296 // Check if this is a2297 const isHidden = fiber.memoizedState !== null;2298 if (isHidden) {2299 // Nested Offscreen tree is already hidden. Don't disappear2300 // its effects.2301 disappearLayoutEffects_complete(subtreeRoot);2302 continue;2303 }2304 break;2305 }2306 }2307 // TODO (Offscreen) Check: subtreeFlags & LayoutStatic2308 if (firstChild !== null) {2309 firstChild.return = fiber;2310 nextEffect = firstChild;2311 } else {2312 disappearLayoutEffects_complete(subtreeRoot);2313 }2314 }2315}2316function disappearLayoutEffects_complete(subtreeRoot: Fiber) {2317 while (nextEffect !== null) {2318 const fiber = nextEffect;2319 if (fiber === subtreeRoot) {2320 nextEffect = null;2321 return;2322 }2323 const sibling = fiber.sibling;2324 if (sibling !== null) {2325 sibling.return = fiber.return;2326 nextEffect = sibling;2327 return;2328 }2329 nextEffect = fiber.return;2330 }2331}2332function reappearLayoutEffects_begin(subtreeRoot: Fiber) {2333 while (nextEffect !== null) {2334 const fiber = nextEffect;2335 const firstChild = fiber.child;2336 if (fiber.tag === OffscreenComponent) {2337 const isHidden = fiber.memoizedState !== null;2338 if (isHidden) {2339 // Nested Offscreen tree is still hidden. Don't re-appear its effects.2340 reappearLayoutEffects_complete(subtreeRoot);2341 continue;2342 }2343 }2344 // TODO (Offscreen) Check: subtreeFlags & LayoutStatic2345 if (firstChild !== null) {2346 // This node may have been reused from a previous render, so we can't2347 // assume its return pointer is correct.2348 firstChild.return = fiber;2349 nextEffect = firstChild;2350 } else {2351 reappearLayoutEffects_complete(subtreeRoot);2352 }2353 }2354}2355function reappearLayoutEffects_complete(subtreeRoot: Fiber) {2356 while (nextEffect !== null) {2357 const fiber = nextEffect;2358 // TODO (Offscreen) Check: flags & LayoutStatic2359 setCurrentDebugFiberInDEV(fiber);2360 try {2361 reappearLayoutEffectsOnFiber(fiber);2362 } catch (error) {2363 reportUncaughtErrorInDEV(error);2364 captureCommitPhaseError(fiber, fiber.return, error);2365 }2366 resetCurrentDebugFiberInDEV();2367 if (fiber === subtreeRoot) {2368 nextEffect = null;2369 return;2370 }2371 const sibling = fiber.sibling;2372 if (sibling !== null) {2373 // This node may have been reused from a previous render, so we can't2374 // assume its return pointer is correct.2375 sibling.return = fiber.return;2376 nextEffect = sibling;2377 return;2378 }2379 nextEffect = fiber.return;2380 }2381}2382export function commitPassiveMountEffects(2383 root: FiberRoot,2384 finishedWork: Fiber,2385): void {2386 nextEffect = finishedWork;2387 commitPassiveMountEffects_begin(finishedWork, root);2388}2389function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {2390 while (nextEffect !== null) {2391 const fiber = nextEffect;2392 const firstChild = fiber.child;2393 if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {2394 ensureCorrectReturnPointer(firstChild, fiber);2395 nextEffect = firstChild;2396 } else {2397 commitPassiveMountEffects_complete(subtreeRoot, root);2398 }2399 }2400}2401function commitPassiveMountEffects_complete(2402 subtreeRoot: Fiber,2403 root: FiberRoot,2404) {2405 while (nextEffect !== null) {2406 const fiber = nextEffect;2407 if ((fiber.flags & Passive) !== NoFlags) {2408 setCurrentDebugFiberInDEV(fiber);2409 try {2410 commitPassiveMountOnFiber(root, fiber);2411 } catch (error) {2412 reportUncaughtErrorInDEV(error);2413 captureCommitPhaseError(fiber, fiber.return, error);2414 }2415 resetCurrentDebugFiberInDEV();2416 }2417 if (fiber === subtreeRoot) {2418 nextEffect = null;2419 return;2420 }2421 const sibling = fiber.sibling;2422 if (sibling !== null) {2423 ensureCorrectReturnPointer(sibling, fiber.return);2424 nextEffect = sibling;2425 return;2426 }2427 nextEffect = fiber.return;2428 }2429}2430function commitPassiveMountOnFiber(2431 finishedRoot: FiberRoot,2432 finishedWork: Fiber,2433): void {2434 switch (finishedWork.tag) {2435 case FunctionComponent:2436 case ForwardRef:2437 case SimpleMemoComponent: {2438 if (2439 enableProfilerTimer &&2440 enableProfilerCommitHooks &&2441 finishedWork.mode & ProfileMode2442 ) {2443 startPassiveEffectTimer();2444 try {2445 commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);2446 } finally {2447 recordPassiveEffectDuration(finishedWork);2448 }2449 } else {2450 commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);2451 }2452 break;2453 }2454 }2455}2456export function commitPassiveUnmountEffects(firstChild: Fiber): void {2457 nextEffect = firstChild;2458 commitPassiveUnmountEffects_begin();2459}2460function commitPassiveUnmountEffects_begin() {2461 while (nextEffect !== null) {2462 const fiber = nextEffect;2463 const child = fiber.child;2464 if ((nextEffect.flags & ChildDeletion) !== NoFlags) {2465 const deletions = fiber.deletions;2466 if (deletions !== null) {2467 for (let i = 0; i < deletions.length; i++) {2468 const fiberToDelete = deletions[i];2469 nextEffect = fiberToDelete;2470 commitPassiveUnmountEffectsInsideOfDeletedTree_begin(2471 fiberToDelete,2472 fiber,2473 );2474 }2475 if (deletedTreeCleanUpLevel >= 1) {2476 // A fiber was deleted from this parent fiber, but it's still part of2477 // the previous (alternate) parent fiber's list of children. Because2478 // children are a linked list, an earlier sibling that's still alive2479 // will be connected to the deleted fiber via its `alternate`:2480 //2481 // live fiber2482 // --alternate--> previous live fiber2483 // --sibling--> deleted fiber2484 //2485 // We can't disconnect `alternate` on nodes that haven't been deleted2486 // yet, but we can disconnect the `sibling` and `child` pointers.2487 const previousFiber = fiber.alternate;2488 if (previousFiber !== null) {2489 let detachedChild = previousFiber.child;2490 if (detachedChild !== null) {2491 previousFiber.child = null;2492 do {2493 const detachedSibling = detachedChild.sibling;2494 detachedChild.sibling = null;2495 detachedChild = detachedSibling;2496 } while (detachedChild !== null);2497 }2498 }2499 }2500 nextEffect = fiber;2501 }2502 }2503 if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {2504 ensureCorrectReturnPointer(child, fiber);2505 nextEffect = child;2506 } else {2507 commitPassiveUnmountEffects_complete();2508 }2509 }2510}2511function commitPassiveUnmountEffects_complete() {2512 while (nextEffect !== null) {2513 const fiber = nextEffect;2514 if ((fiber.flags & Passive) !== NoFlags) {2515 setCurrentDebugFiberInDEV(fiber);2516 commitPassiveUnmountOnFiber(fiber);2517 resetCurrentDebugFiberInDEV();2518 }2519 const sibling = fiber.sibling;2520 if (sibling !== null) {2521 ensureCorrectReturnPointer(sibling, fiber.return);2522 nextEffect = sibling;2523 return;2524 }2525 nextEffect = fiber.return;2526 }2527}2528function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {2529 switch (finishedWork.tag) {2530 case FunctionComponent:2531 case ForwardRef:2532 case SimpleMemoComponent: {2533 if (2534 enableProfilerTimer &&2535 enableProfilerCommitHooks &&2536 finishedWork.mode & ProfileMode2537 ) {2538 startPassiveEffectTimer();2539 commitHookEffectListUnmount(2540 HookPassive | HookHasEffect,2541 finishedWork,2542 finishedWork.return,2543 );2544 recordPassiveEffectDuration(finishedWork);2545 } else {2546 commitHookEffectListUnmount(2547 HookPassive | HookHasEffect,2548 finishedWork,2549 finishedWork.return,2550 );2551 }2552 break;2553 }2554 }2555}2556function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(2557 deletedSubtreeRoot: Fiber,2558 nearestMountedAncestor: Fiber | null,2559) {2560 while (nextEffect !== null) {2561 const fiber = nextEffect;2562 // Deletion effects fire in parent -> child order2563 // TODO: Check if fiber has a PassiveStatic flag2564 setCurrentDebugFiberInDEV(fiber);2565 commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);2566 resetCurrentDebugFiberInDEV();2567 const child = fiber.child;2568 // TODO: Only traverse subtree if it has a PassiveStatic flag. (But, if we2569 // do this, still need to handle `deletedTreeCleanUpLevel` correctly.)2570 if (child !== null) {2571 ensureCorrectReturnPointer(child, fiber);2572 nextEffect = child;2573 } else {2574 commitPassiveUnmountEffectsInsideOfDeletedTree_complete(2575 deletedSubtreeRoot,2576 );2577 }2578 }2579}2580function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(2581 deletedSubtreeRoot: Fiber,2582) {2583 while (nextEffect !== null) {2584 const fiber = nextEffect;2585 const sibling = fiber.sibling;2586 const returnFiber = fiber.return;2587 if (deletedTreeCleanUpLevel >= 2) {2588 // Recursively traverse the entire deleted tree and clean up fiber fields.2589 // This is more aggressive than ideal, and the long term goal is to only2590 // have to detach the deleted tree at the root.2591 detachFiberAfterEffects(fiber);2592 if (fiber === deletedSubtreeRoot) {2593 nextEffect = null;2594 return;2595 }2596 } else {2597 // This is the default branch (level 0). We do not recursively clear all2598 // the fiber fields. Only the root of the deleted subtree.2599 if (fiber === deletedSubtreeRoot) {2600 detachFiberAfterEffects(fiber);2601 nextEffect = null;2602 return;2603 }2604 }2605 if (sibling !== null) {2606 ensureCorrectReturnPointer(sibling, returnFiber);2607 nextEffect = sibling;2608 return;2609 }2610 nextEffect = returnFiber;2611 }2612}2613function commitPassiveUnmountInsideDeletedTreeOnFiber(2614 current: Fiber,2615 nearestMountedAncestor: Fiber | null,2616): void {2617 switch (current.tag) {2618 case FunctionComponent:2619 case ForwardRef:2620 case SimpleMemoComponent: {2621 if (2622 enableProfilerTimer &&2623 enableProfilerCommitHooks &&2624 current.mode & ProfileMode2625 ) {2626 startPassiveEffectTimer();2627 commitHookEffectListUnmount(2628 HookPassive,2629 current,2630 nearestMountedAncestor,2631 );2632 recordPassiveEffectDuration(current);2633 } else {2634 commitHookEffectListUnmount(2635 HookPassive,2636 current,2637 nearestMountedAncestor,2638 );2639 }2640 break;2641 }2642 }2643}2644let didWarnWrongReturnPointer = false;2645function ensureCorrectReturnPointer(fiber, expectedReturnFiber) {2646 if (__DEV__) {2647 if (!didWarnWrongReturnPointer && fiber.return !== expectedReturnFiber) {2648 didWarnWrongReturnPointer = true;2649 console.error(2650 'Internal React error: Return pointer is inconsistent ' +2651 'with parent.',2652 );2653 }2654 }2655 // TODO: Remove this assignment once we're confident that it won't break2656 // anything, by checking the warning logs for the above invariant2657 fiber.return = expectedReturnFiber;2658}2659// TODO: Reuse reappearLayoutEffects traversal here?2660function invokeLayoutEffectMountInDEV(fiber: Fiber): void {2661 if (__DEV__ && enableStrictEffects) {2662 // We don't need to re-check StrictEffectsMode here.2663 // This function is only called if that check has already passed.2664 switch (fiber.tag) {2665 case FunctionComponent:2666 case ForwardRef:2667 case SimpleMemoComponent: {2668 try {2669 commitHookEffectListMount(HookLayout | HookHasEffect, fiber);2670 } catch (error) {2671 reportUncaughtErrorInDEV(error);2672 captureCommitPhaseError(fiber, fiber.return, error);2673 }2674 break;2675 }2676 case ClassComponent: {2677 const instance = fiber.stateNode;2678 try {2679 instance.componentDidMount();2680 } catch (error) {2681 reportUncaughtErrorInDEV(error);2682 captureCommitPhaseError(fiber, fiber.return, error);2683 }2684 break;2685 }2686 }2687 }2688}2689function invokePassiveEffectMountInDEV(fiber: Fiber): void {2690 if (__DEV__ && enableStrictEffects) {2691 // We don't need to re-check StrictEffectsMode here.2692 // This function is only called if that check has already passed.2693 switch (fiber.tag) {2694 case FunctionComponent:2695 case ForwardRef:2696 case SimpleMemoComponent: {2697 try {2698 commitHookEffectListMount(HookPassive | HookHasEffect, fiber);2699 } catch (error) {2700 reportUncaughtErrorInDEV(error);2701 captureCommitPhaseError(fiber, fiber.return, error);2702 }2703 break;2704 }2705 }2706 }2707}2708function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {2709 if (__DEV__ && enableStrictEffects) {2710 // We don't need to re-check StrictEffectsMode here.2711 // This function is only called if that check has already passed.2712 switch (fiber.tag) {2713 case FunctionComponent:2714 case ForwardRef:2715 case SimpleMemoComponent: {2716 try {2717 commitHookEffectListUnmount(2718 HookLayout | HookHasEffect,2719 fiber,2720 fiber.return,2721 );2722 } catch (error) {2723 reportUncaughtErrorInDEV(error);2724 captureCommitPhaseError(fiber, fiber.return, error);2725 }2726 break;2727 }2728 case ClassComponent: {2729 const instance = fiber.stateNode;2730 if (typeof instance.componentWillUnmount === 'function') {2731 safelyCallComponentWillUnmount(fiber, fiber.return, instance);2732 }2733 break;2734 }2735 }2736 }2737}2738function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {2739 if (__DEV__ && enableStrictEffects) {2740 // We don't need to re-check StrictEffectsMode here.2741 // This function is only called if that check has already passed.2742 switch (fiber.tag) {2743 case FunctionComponent:2744 case ForwardRef:2745 case SimpleMemoComponent: {2746 try {2747 commitHookEffectListUnmount(2748 HookPassive | HookHasEffect,2749 fiber,2750 fiber.return,2751 );2752 } catch (error) {2753 reportUncaughtErrorInDEV(error);2754 captureCommitPhaseError(fiber, fiber.return, error);2755 }2756 }2757 }2758 }2759}2760export {2761 commitResetTextContent,...
ReactFiberCommitWork.new.js
Source:ReactFiberCommitWork.new.js
...314 'This unit of work tag should not have side-effects. This error is ' +315 'likely caused by a bug in React. Please file an issue.',316 );317}318function commitHookEffectListUnmount(319 flags ,320 finishedWork ,321 nearestMountedAncestor ,322) {323 const updateQueue = (finishedWork.updateQueue );324 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;325 if (lastEffect !== null) {326 const firstEffect = lastEffect.next;327 let effect = firstEffect;328 do {329 if ((effect.tag & flags) === flags) {330 // Unmount331 const destroy = effect.destroy;332 effect.destroy = undefined;333 if (destroy !== undefined) {334 safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);335 }336 }337 effect = effect.next;338 } while (effect !== firstEffect);339 }340}341function commitHookEffectListMount(flags , finishedWork ) {342 const updateQueue = (finishedWork.updateQueue );343 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;344 if (lastEffect !== null) {345 const firstEffect = lastEffect.next;346 let effect = firstEffect;347 do {348 if ((effect.tag & flags) === flags) {349 // Mount350 const create = effect.create;351 effect.destroy = create();352 if (__DEV__) {353 const destroy = effect.destroy;354 if (destroy !== undefined && typeof destroy !== 'function') {355 let addendum;356 if (destroy === null) {357 addendum =358 ' You returned null. If your effect does not require clean ' +359 'up, return undefined (or nothing).';360 } else if (typeof destroy.then === 'function') {361 addendum =362 '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +363 'Instead, write the async function inside your effect ' +364 'and call it immediately:\n\n' +365 'useEffect(() => {\n' +366 ' async function fetchData() {\n' +367 ' // You can await here\n' +368 ' const response = await MyAPI.getData(someId);\n' +369 ' // ...\n' +370 ' }\n' +371 ' fetchData();\n' +372 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +373 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';374 } else {375 addendum = ' You returned: ' + destroy;376 }377 console.error(378 'An effect function must not return anything besides a function, ' +379 'which is used for clean-up.%s',380 addendum,381 );382 }383 }384 }385 effect = effect.next;386 } while (effect !== firstEffect);387 }388}389function commitProfilerPassiveEffect(390 finishedRoot ,391 finishedWork ,392) {393 if (enableProfilerTimer && enableProfilerCommitHooks) {394 switch (finishedWork.tag) {395 case Profiler: {396 const {passiveEffectDuration} = finishedWork.stateNode;397 const {id, onPostCommit} = finishedWork.memoizedProps;398 // This value will still reflect the previous commit phase.399 // It does not get reset until the start of the next commit phase.400 const commitTime = getCommitTime();401 if (typeof onPostCommit === 'function') {402 if (enableSchedulerTracing) {403 onPostCommit(404 id,405 finishedWork.alternate === null ? 'mount' : 'update',406 passiveEffectDuration,407 commitTime,408 finishedRoot.memoizedInteractions,409 );410 } else {411 onPostCommit(412 id,413 finishedWork.alternate === null ? 'mount' : 'update',414 passiveEffectDuration,415 commitTime,416 );417 }418 }419 break;420 }421 default:422 break;423 }424 }425}426function recursivelyCommitLayoutEffects(427 finishedWork ,428 finishedRoot ,429) {430 const {flags, tag} = finishedWork;431 switch (tag) {432 case Profiler: {433 let prevProfilerOnStack = null;434 if (enableProfilerTimer && enableProfilerCommitHooks) {435 prevProfilerOnStack = nearestProfilerOnStack;436 nearestProfilerOnStack = finishedWork;437 }438 let child = finishedWork.child;439 while (child !== null) {440 const primarySubtreeFlags = finishedWork.subtreeFlags & LayoutMask;441 if (primarySubtreeFlags !== NoFlags) {442 if (__DEV__) {443 const prevCurrentFiberInDEV = currentDebugFiberInDEV;444 setCurrentDebugFiberInDEV(child);445 invokeGuardedCallback(446 null,447 recursivelyCommitLayoutEffects,448 null,449 child,450 finishedRoot,451 );452 if (hasCaughtError()) {453 const error = clearCaughtError();454 captureCommitPhaseError(child, finishedWork, error);455 }456 if (prevCurrentFiberInDEV !== null) {457 setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);458 } else {459 resetCurrentDebugFiberInDEV();460 }461 } else {462 try {463 recursivelyCommitLayoutEffects(child, finishedRoot);464 } catch (error) {465 captureCommitPhaseError(child, finishedWork, error);466 }467 }468 }469 child = child.sibling;470 }471 const primaryFlags = flags & (Update | Callback);472 if (primaryFlags !== NoFlags) {473 if (enableProfilerTimer) {474 if (__DEV__) {475 const prevCurrentFiberInDEV = currentDebugFiberInDEV;476 setCurrentDebugFiberInDEV(finishedWork);477 invokeGuardedCallback(478 null,479 commitLayoutEffectsForProfiler,480 null,481 finishedWork,482 finishedRoot,483 );484 if (hasCaughtError()) {485 const error = clearCaughtError();486 captureCommitPhaseError(finishedWork, finishedWork.return, error);487 }488 if (prevCurrentFiberInDEV !== null) {489 setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);490 } else {491 resetCurrentDebugFiberInDEV();492 }493 } else {494 try {495 commitLayoutEffectsForProfiler(finishedWork, finishedRoot);496 } catch (error) {497 captureCommitPhaseError(finishedWork, finishedWork.return, error);498 }499 }500 }501 }502 if (enableProfilerTimer && enableProfilerCommitHooks) {503 // Propagate layout effect durations to the next nearest Profiler ancestor.504 // Do not reset these values until the next render so DevTools has a chance to read them first.505 if (prevProfilerOnStack !== null) {506 prevProfilerOnStack.stateNode.effectDuration +=507 finishedWork.stateNode.effectDuration;508 }509 nearestProfilerOnStack = prevProfilerOnStack;510 }511 break;512 }513 // case Offscreen: {514 // TODO: Fast path to invoke all nested layout effects when Offscren goes from hidden to visible.515 // break;516 // }517 default: {518 let child = finishedWork.child;519 while (child !== null) {520 const primarySubtreeFlags = finishedWork.subtreeFlags & LayoutMask;521 if (primarySubtreeFlags !== NoFlags) {522 if (__DEV__) {523 const prevCurrentFiberInDEV = currentDebugFiberInDEV;524 setCurrentDebugFiberInDEV(child);525 invokeGuardedCallback(526 null,527 recursivelyCommitLayoutEffects,528 null,529 child,530 finishedRoot,531 );532 if (hasCaughtError()) {533 const error = clearCaughtError();534 captureCommitPhaseError(child, finishedWork, error);535 }536 if (prevCurrentFiberInDEV !== null) {537 setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);538 } else {539 resetCurrentDebugFiberInDEV();540 }541 } else {542 try {543 recursivelyCommitLayoutEffects(child, finishedRoot);544 } catch (error) {545 captureCommitPhaseError(child, finishedWork, error);546 }547 }548 }549 child = child.sibling;550 }551 const primaryFlags = flags & (Update | Callback);552 if (primaryFlags !== NoFlags) {553 switch (tag) {554 case FunctionComponent:555 case ForwardRef:556 case SimpleMemoComponent:557 case Block: {558 if (559 enableProfilerTimer &&560 enableProfilerCommitHooks &&561 finishedWork.mode & ProfileMode562 ) {563 try {564 startLayoutEffectTimer();565 commitHookEffectListMount(566 HookLayout | HookHasEffect,567 finishedWork,568 );569 } finally {570 recordLayoutEffectDuration(finishedWork);571 }572 } else {573 commitHookEffectListMount(574 HookLayout | HookHasEffect,575 finishedWork,576 );577 }578 if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags) {579 schedulePassiveEffectCallback();580 }581 break;582 }583 case ClassComponent: {584 // NOTE: Layout effect durations are measured within this function.585 commitLayoutEffectsForClassComponent(finishedWork);586 break;587 }588 case HostRoot: {589 commitLayoutEffectsForHostRoot(finishedWork);590 break;591 }592 case HostComponent: {593 commitLayoutEffectsForHostComponent(finishedWork);594 break;595 }596 case SuspenseComponent: {597 commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);598 break;599 }600 case FundamentalComponent:601 case HostPortal:602 case HostText:603 case IncompleteClassComponent:604 case LegacyHiddenComponent:605 case OffscreenComponent:606 case ScopeComponent:607 case SuspenseListComponent: {608 // We have no life-cycles associated with these component types.609 break;610 }611 default: {612 invariant(613 false,614 'This unit of work tag should not have side-effects. This error is ' +615 'likely caused by a bug in React. Please file an issue.',616 );617 }618 }619 }620 if (enableScopeAPI) {621 // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.622 if (flags & Ref && tag !== ScopeComponent) {623 commitAttachRef(finishedWork);624 }625 } else {626 if (flags & Ref) {627 commitAttachRef(finishedWork);628 }629 }630 break;631 }632 }633}634function commitLayoutEffectsForProfiler(635 finishedWork ,636 finishedRoot ,637) {638 if (enableProfilerTimer) {639 const flags = finishedWork.flags;640 const current = finishedWork.alternate;641 const {onCommit, onRender} = finishedWork.memoizedProps;642 const {effectDuration} = finishedWork.stateNode;643 const commitTime = getCommitTime();644 const OnRenderFlag = Update;645 const OnCommitFlag = Callback;646 if ((flags & OnRenderFlag) !== NoFlags && typeof onRender === 'function') {647 if (enableSchedulerTracing) {648 onRender(649 finishedWork.memoizedProps.id,650 current === null ? 'mount' : 'update',651 finishedWork.actualDuration,652 finishedWork.treeBaseDuration,653 finishedWork.actualStartTime,654 commitTime,655 finishedRoot.memoizedInteractions,656 );657 } else {658 onRender(659 finishedWork.memoizedProps.id,660 current === null ? 'mount' : 'update',661 finishedWork.actualDuration,662 finishedWork.treeBaseDuration,663 finishedWork.actualStartTime,664 commitTime,665 );666 }667 }668 if (enableProfilerCommitHooks) {669 if (670 (flags & OnCommitFlag) !== NoFlags &&671 typeof onCommit === 'function'672 ) {673 if (enableSchedulerTracing) {674 onCommit(675 finishedWork.memoizedProps.id,676 current === null ? 'mount' : 'update',677 effectDuration,678 commitTime,679 finishedRoot.memoizedInteractions,680 );681 } else {682 onCommit(683 finishedWork.memoizedProps.id,684 current === null ? 'mount' : 'update',685 effectDuration,686 commitTime,687 );688 }689 }690 }691 }692}693function commitLayoutEffectsForClassComponent(finishedWork ) {694 const instance = finishedWork.stateNode;695 const current = finishedWork.alternate;696 if (finishedWork.flags & Update) {697 if (current === null) {698 // We could update instance props and state here,699 // but instead we rely on them being set during last render.700 // TODO: revisit this when we implement resuming.701 if (__DEV__) {702 if (703 finishedWork.type === finishedWork.elementType &&704 !didWarnAboutReassigningProps705 ) {706 if (instance.props !== finishedWork.memoizedProps) {707 console.error(708 'Expected %s props to match memoized props before ' +709 'componentDidMount. ' +710 'This might either be because of a bug in React, or because ' +711 'a component reassigns its own `this.props`. ' +712 'Please file an issue.',713 getComponentName(finishedWork.type) || 'instance',714 );715 }716 if (instance.state !== finishedWork.memoizedState) {717 console.error(718 'Expected %s state to match memoized state before ' +719 'componentDidMount. ' +720 'This might either be because of a bug in React, or because ' +721 'a component reassigns its own `this.state`. ' +722 'Please file an issue.',723 getComponentName(finishedWork.type) || 'instance',724 );725 }726 }727 }728 if (729 enableProfilerTimer &&730 enableProfilerCommitHooks &&731 finishedWork.mode & ProfileMode732 ) {733 try {734 startLayoutEffectTimer();735 instance.componentDidMount();736 } finally {737 recordLayoutEffectDuration(finishedWork);738 }739 } else {740 instance.componentDidMount();741 }742 } else {743 const prevProps =744 finishedWork.elementType === finishedWork.type745 ? current.memoizedProps746 : resolveDefaultProps(finishedWork.type, current.memoizedProps);747 const prevState = current.memoizedState;748 // We could update instance props and state here,749 // but instead we rely on them being set during last render.750 // TODO: revisit this when we implement resuming.751 if (__DEV__) {752 if (753 finishedWork.type === finishedWork.elementType &&754 !didWarnAboutReassigningProps755 ) {756 if (instance.props !== finishedWork.memoizedProps) {757 console.error(758 'Expected %s props to match memoized props before ' +759 'componentDidUpdate. ' +760 'This might either be because of a bug in React, or because ' +761 'a component reassigns its own `this.props`. ' +762 'Please file an issue.',763 getComponentName(finishedWork.type) || 'instance',764 );765 }766 if (instance.state !== finishedWork.memoizedState) {767 console.error(768 'Expected %s state to match memoized state before ' +769 'componentDidUpdate. ' +770 'This might either be because of a bug in React, or because ' +771 'a component reassigns its own `this.state`. ' +772 'Please file an issue.',773 getComponentName(finishedWork.type) || 'instance',774 );775 }776 }777 }778 if (779 enableProfilerTimer &&780 enableProfilerCommitHooks &&781 finishedWork.mode & ProfileMode782 ) {783 try {784 startLayoutEffectTimer();785 instance.componentDidUpdate(786 prevProps,787 prevState,788 instance.__reactInternalSnapshotBeforeUpdate,789 );790 } finally {791 recordLayoutEffectDuration(finishedWork);792 }793 } else {794 instance.componentDidUpdate(795 prevProps,796 prevState,797 instance.__reactInternalSnapshotBeforeUpdate,798 );799 }800 }801 }802 // TODO: I think this is now always non-null by the time it reaches the803 // commit phase. Consider removing the type check.804 const updateQueue = (finishedWork.updateQueue );805 if (updateQueue !== null) {806 if (__DEV__) {807 if (808 finishedWork.type === finishedWork.elementType &&809 !didWarnAboutReassigningProps810 ) {811 if (instance.props !== finishedWork.memoizedProps) {812 console.error(813 'Expected %s props to match memoized props before ' +814 'processing the update queue. ' +815 'This might either be because of a bug in React, or because ' +816 'a component reassigns its own `this.props`. ' +817 'Please file an issue.',818 getComponentName(finishedWork.type) || 'instance',819 );820 }821 if (instance.state !== finishedWork.memoizedState) {822 console.error(823 'Expected %s state to match memoized state before ' +824 'processing the update queue. ' +825 'This might either be because of a bug in React, or because ' +826 'a component reassigns its own `this.state`. ' +827 'Please file an issue.',828 getComponentName(finishedWork.type) || 'instance',829 );830 }831 }832 }833 // We could update instance props and state here,834 // but instead we rely on them being set during last render.835 // TODO: revisit this when we implement resuming.836 commitUpdateQueue(finishedWork, updateQueue, instance);837 }838}839function commitLayoutEffectsForHostRoot(finishedWork ) {840 // TODO: I think this is now always non-null by the time it reaches the841 // commit phase. Consider removing the type check.842 const updateQueue = (finishedWork.updateQueue );843 if (updateQueue !== null) {844 let instance = null;845 if (finishedWork.child !== null) {846 switch (finishedWork.child.tag) {847 case HostComponent:848 instance = getPublicInstance(finishedWork.child.stateNode);849 break;850 case ClassComponent:851 instance = finishedWork.child.stateNode;852 break;853 }854 }855 commitUpdateQueue(finishedWork, updateQueue, instance);856 }857}858function commitLayoutEffectsForHostComponent(finishedWork ) {859 const instance = finishedWork.stateNode;860 const current = finishedWork.alternate;861 // Renderers may schedule work to be done after host components are mounted862 // (eg DOM renderer may schedule auto-focus for inputs and form controls).863 // These effects should only be committed when components are first mounted,864 // aka when there is no current/alternate.865 if (current === null && finishedWork.flags & Update) {866 const type = finishedWork.type;867 const props = finishedWork.memoizedProps;868 commitMount(instance, type, props, finishedWork);869 }870}871function hideOrUnhideAllChildren(finishedWork, isHidden) {872 if (supportsMutation) {873 // We only have the top Fiber that was inserted but we need to recurse down its874 // children to find all the terminal nodes.875 let node = finishedWork;876 while (true) {877 if (node.tag === HostComponent) {878 const instance = node.stateNode;879 if (isHidden) {880 hideInstance(instance);881 } else {882 unhideInstance(node.stateNode, node.memoizedProps);883 }884 } else if (node.tag === HostText) {885 const instance = node.stateNode;886 if (isHidden) {887 hideTextInstance(instance);888 } else {889 unhideTextInstance(instance, node.memoizedProps);890 }891 } else if (892 (node.tag === OffscreenComponent ||893 node.tag === LegacyHiddenComponent) &&894 (node.memoizedState ) !== null &&895 node !== finishedWork896 ) {897 // Found a nested Offscreen component that is hidden. Don't search898 // any deeper. This tree should remain hidden.899 } else if (node.child !== null) {900 node.child.return = node;901 node = node.child;902 continue;903 }904 if (node === finishedWork) {905 return;906 }907 while (node.sibling === null) {908 if (node.return === null || node.return === finishedWork) {909 return;910 }911 node = node.return;912 }913 node.sibling.return = node.return;914 node = node.sibling;915 }916 }917}918function commitAttachRef(finishedWork ) {919 const ref = finishedWork.ref;920 if (ref !== null) {921 const instance = finishedWork.stateNode;922 let instanceToUse;923 switch (finishedWork.tag) {924 case HostComponent:925 instanceToUse = getPublicInstance(instance);926 break;927 default:928 instanceToUse = instance;929 }930 // Moved outside to ensure DCE works with this flag931 if (enableScopeAPI && finishedWork.tag === ScopeComponent) {932 instanceToUse = instance;933 }934 if (typeof ref === 'function') {935 ref(instanceToUse);936 } else {937 if (__DEV__) {938 if (!ref.hasOwnProperty('current')) {939 console.error(940 'Unexpected ref object provided for %s. ' +941 'Use either a ref-setter function or React.createRef().',942 getComponentName(finishedWork.type),943 );944 }945 }946 ref.current = instanceToUse;947 }948 }949}950function commitDetachRef(current ) {951 const currentRef = current.ref;952 if (currentRef !== null) {953 if (typeof currentRef === 'function') {954 currentRef(null);955 } else {956 currentRef.current = null;957 }958 }959}960// User-originating errors (lifecycles and refs) should not interrupt961// deletion, so don't let them throw. Host-originating errors should962// interrupt deletion, so it's okay963function commitUnmount(964 finishedRoot ,965 current ,966 nearestMountedAncestor ,967 renderPriorityLevel ,968) {969 onCommitUnmount(current);970 switch (current.tag) {971 case FunctionComponent:972 case ForwardRef:973 case MemoComponent:974 case SimpleMemoComponent:975 case Block: {976 const updateQueue = (current.updateQueue );977 if (updateQueue !== null) {978 const lastEffect = updateQueue.lastEffect;979 if (lastEffect !== null) {980 const firstEffect = lastEffect.next;981 let effect = firstEffect;982 do {983 const {destroy, tag} = effect;984 if (destroy !== undefined) {985 if ((tag & HookLayout) !== NoHookEffect) {986 if (987 enableProfilerTimer &&988 enableProfilerCommitHooks &&989 current.mode & ProfileMode990 ) {991 startLayoutEffectTimer();992 safelyCallDestroy(current, nearestMountedAncestor, destroy);993 recordLayoutEffectDuration(current);994 } else {995 safelyCallDestroy(current, nearestMountedAncestor, destroy);996 }997 }998 }999 effect = effect.next;1000 } while (effect !== firstEffect);1001 }1002 }1003 return;1004 }1005 case ClassComponent: {1006 safelyDetachRef(current, nearestMountedAncestor);1007 const instance = current.stateNode;1008 if (typeof instance.componentWillUnmount === 'function') {1009 safelyCallComponentWillUnmount(1010 current,1011 instance,1012 nearestMountedAncestor,1013 );1014 }1015 return;1016 }1017 case HostComponent: {1018 safelyDetachRef(current, nearestMountedAncestor);1019 return;1020 }1021 case HostPortal: {1022 // TODO: this is recursive.1023 // We are also not using this parent because1024 // the portal will get pushed immediately.1025 if (supportsMutation) {1026 unmountHostComponents(1027 finishedRoot,1028 current,1029 nearestMountedAncestor,1030 renderPriorityLevel,1031 );1032 } else if (supportsPersistence) {1033 emptyPortalContainer(current);1034 }1035 return;1036 }1037 case FundamentalComponent: {1038 if (enableFundamentalAPI) {1039 const fundamentalInstance = current.stateNode;1040 if (fundamentalInstance !== null) {1041 unmountFundamentalComponent(fundamentalInstance);1042 current.stateNode = null;1043 }1044 }1045 return;1046 }1047 case DehydratedFragment: {1048 if (enableSuspenseCallback) {1049 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1050 if (hydrationCallbacks !== null) {1051 const onDeleted = hydrationCallbacks.onDeleted;1052 if (onDeleted) {1053 onDeleted((current.stateNode ));1054 }1055 }1056 }1057 return;1058 }1059 case ScopeComponent: {1060 if (enableScopeAPI) {1061 safelyDetachRef(current, nearestMountedAncestor);1062 }1063 return;1064 }1065 }1066}1067function commitNestedUnmounts(1068 finishedRoot ,1069 root ,1070 nearestMountedAncestor ,1071 renderPriorityLevel ,1072) {1073 // While we're inside a removed host node we don't want to call1074 // removeChild on the inner nodes because they're removed by the top1075 // call anyway. We also want to call componentWillUnmount on all1076 // composites before this host node is removed from the tree. Therefore1077 // we do an inner loop while we're still inside the host node.1078 let node = root;1079 while (true) {1080 commitUnmount(1081 finishedRoot,1082 node,1083 nearestMountedAncestor,1084 renderPriorityLevel,1085 );1086 // Visit children because they may contain more composite or host nodes.1087 // Skip portals because commitUnmount() currently visits them recursively.1088 if (1089 node.child !== null &&1090 // If we use mutation we drill down into portals using commitUnmount above.1091 // If we don't use mutation we drill down into portals here instead.1092 (!supportsMutation || node.tag !== HostPortal)1093 ) {1094 node.child.return = node;1095 node = node.child;1096 continue;1097 }1098 if (node === root) {1099 return;1100 }1101 while (node.sibling === null) {1102 if (node.return === null || node.return === root) {1103 return;1104 }1105 node = node.return;1106 }1107 node.sibling.return = node.return;1108 node = node.sibling;1109 }1110}1111function detachFiberMutation(fiber ) {1112 // Cut off the return pointer to disconnect it from the tree.1113 // This enables us to detect and warn against state updates on an unmounted component.1114 // It also prevents events from bubbling from within disconnected components.1115 //1116 // Ideally, we should also clear the child pointer of the parent alternate to let this1117 // get GC:ed but we don't know which for sure which parent is the current1118 // one so we'll settle for GC:ing the subtree of this child.1119 // This child itself will be GC:ed when the parent updates the next time.1120 //1121 // Note that we can't clear child or sibling pointers yet.1122 // They're needed for passive effects and for findDOMNode.1123 // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).1124 const alternate = fiber.alternate;1125 if (alternate !== null) {1126 alternate.return = null;1127 fiber.alternate = null;1128 }1129 fiber.return = null;1130}1131function emptyPortalContainer(current ) {1132 if (!supportsPersistence) {1133 return;1134 }1135 const portal 1136 1137 1138 1139 = current.stateNode;1140 const {containerInfo} = portal;1141 const emptyChildSet = createContainerChildSet(containerInfo);1142 replaceContainerChildren(containerInfo, emptyChildSet);1143}1144function commitContainer(finishedWork ) {1145 if (!supportsPersistence) {1146 return;1147 }1148 switch (finishedWork.tag) {1149 case ClassComponent:1150 case HostComponent:1151 case HostText:1152 case FundamentalComponent: {1153 return;1154 }1155 case HostRoot:1156 case HostPortal: {1157 const portalOrRoot 1158 1159 1160 1161 = finishedWork.stateNode;1162 const {containerInfo, pendingChildren} = portalOrRoot;1163 replaceContainerChildren(containerInfo, pendingChildren);1164 return;1165 }1166 }1167 invariant(1168 false,1169 'This unit of work tag should not have side-effects. This error is ' +1170 'likely caused by a bug in React. Please file an issue.',1171 );1172}1173function getHostParentFiber(fiber ) {1174 let parent = fiber.return;1175 while (parent !== null) {1176 if (isHostParent(parent)) {1177 return parent;1178 }1179 parent = parent.return;1180 }1181 invariant(1182 false,1183 'Expected to find a host parent. This error is likely caused by a bug ' +1184 'in React. Please file an issue.',1185 );1186}1187function isHostParent(fiber ) {1188 return (1189 fiber.tag === HostComponent ||1190 fiber.tag === HostRoot ||1191 fiber.tag === HostPortal1192 );1193}1194function getHostSibling(fiber ) {1195 // We're going to search forward into the tree until we find a sibling host1196 // node. Unfortunately, if multiple insertions are done in a row we have to1197 // search past them. This leads to exponential search for the next sibling.1198 // TODO: Find a more efficient way to do this.1199 let node = fiber;1200 siblings: while (true) {1201 // If we didn't find anything, let's try the next sibling.1202 while (node.sibling === null) {1203 if (node.return === null || isHostParent(node.return)) {1204 // If we pop out of the root or hit the parent the fiber we are the1205 // last sibling.1206 return null;1207 }1208 node = node.return;1209 }1210 node.sibling.return = node.return;1211 node = node.sibling;1212 while (1213 node.tag !== HostComponent &&1214 node.tag !== HostText &&1215 node.tag !== DehydratedFragment1216 ) {1217 // If it is not host node and, we might have a host node inside it.1218 // Try to search down until we find one.1219 if (node.flags & Placement) {1220 // If we don't have a child, try the siblings instead.1221 continue siblings;1222 }1223 // If we don't have a child, try the siblings instead.1224 // We also skip portals because they are not part of this host tree.1225 if (node.child === null || node.tag === HostPortal) {1226 continue siblings;1227 } else {1228 node.child.return = node;1229 node = node.child;1230 }1231 }1232 // Check if this host node is stable or about to be placed.1233 if (!(node.flags & Placement)) {1234 // Found it!1235 return node.stateNode;1236 }1237 }1238}1239function commitPlacement(finishedWork ) {1240 if (!supportsMutation) {1241 return;1242 }1243 // Recursively insert all host nodes into the parent.1244 const parentFiber = getHostParentFiber(finishedWork);1245 // Note: these two variables *must* always be updated together.1246 let parent;1247 let isContainer;1248 const parentStateNode = parentFiber.stateNode;1249 switch (parentFiber.tag) {1250 case HostComponent:1251 parent = parentStateNode;1252 isContainer = false;1253 break;1254 case HostRoot:1255 parent = parentStateNode.containerInfo;1256 isContainer = true;1257 break;1258 case HostPortal:1259 parent = parentStateNode.containerInfo;1260 isContainer = true;1261 break;1262 case FundamentalComponent:1263 if (enableFundamentalAPI) {1264 parent = parentStateNode.instance;1265 isContainer = false;1266 }1267 // eslint-disable-next-line-no-fallthrough1268 default:1269 invariant(1270 false,1271 'Invalid host parent fiber. This error is likely caused by a bug ' +1272 'in React. Please file an issue.',1273 );1274 }1275 if (parentFiber.flags & ContentReset) {1276 // Reset the text content of the parent before doing any insertions1277 resetTextContent(parent);1278 // Clear ContentReset from the effect tag1279 parentFiber.flags &= ~ContentReset;1280 }1281 const before = getHostSibling(finishedWork);1282 // We only have the top Fiber that was inserted but we need to recurse down its1283 // children to find all the terminal nodes.1284 if (isContainer) {1285 insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);1286 } else {1287 insertOrAppendPlacementNode(finishedWork, before, parent);1288 }1289}1290function insertOrAppendPlacementNodeIntoContainer(1291 node ,1292 before ,1293 parent ,1294) {1295 const {tag} = node;1296 const isHost = tag === HostComponent || tag === HostText;1297 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1298 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1299 if (before) {1300 insertInContainerBefore(parent, stateNode, before);1301 } else {1302 appendChildToContainer(parent, stateNode);1303 }1304 } else if (tag === HostPortal) {1305 // If the insertion itself is a portal, then we don't want to traverse1306 // down its children. Instead, we'll get insertions from each child in1307 // the portal directly.1308 } else {1309 const child = node.child;1310 if (child !== null) {1311 insertOrAppendPlacementNodeIntoContainer(child, before, parent);1312 let sibling = child.sibling;1313 while (sibling !== null) {1314 insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);1315 sibling = sibling.sibling;1316 }1317 }1318 }1319}1320function insertOrAppendPlacementNode(1321 node ,1322 before ,1323 parent ,1324) {1325 const {tag} = node;1326 const isHost = tag === HostComponent || tag === HostText;1327 if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {1328 const stateNode = isHost ? node.stateNode : node.stateNode.instance;1329 if (before) {1330 insertBefore(parent, stateNode, before);1331 } else {1332 appendChild(parent, stateNode);1333 }1334 } else if (tag === HostPortal) {1335 // If the insertion itself is a portal, then we don't want to traverse1336 // down its children. Instead, we'll get insertions from each child in1337 // the portal directly.1338 } else {1339 const child = node.child;1340 if (child !== null) {1341 insertOrAppendPlacementNode(child, before, parent);1342 let sibling = child.sibling;1343 while (sibling !== null) {1344 insertOrAppendPlacementNode(sibling, before, parent);1345 sibling = sibling.sibling;1346 }1347 }1348 }1349}1350function unmountHostComponents(1351 finishedRoot ,1352 current ,1353 nearestMountedAncestor ,1354 renderPriorityLevel ,1355) {1356 // We only have the top Fiber that was deleted but we need to recurse down its1357 // children to find all the terminal nodes.1358 let node = current;1359 // Each iteration, currentParent is populated with node's host parent if not1360 // currentParentIsValid.1361 let currentParentIsValid = false;1362 // Note: these two variables *must* always be updated together.1363 let currentParent;1364 let currentParentIsContainer;1365 while (true) {1366 if (!currentParentIsValid) {1367 let parent = node.return;1368 findParent: while (true) {1369 invariant(1370 parent !== null,1371 'Expected to find a host parent. This error is likely caused by ' +1372 'a bug in React. Please file an issue.',1373 );1374 const parentStateNode = parent.stateNode;1375 switch (parent.tag) {1376 case HostComponent:1377 currentParent = parentStateNode;1378 currentParentIsContainer = false;1379 break findParent;1380 case HostRoot:1381 currentParent = parentStateNode.containerInfo;1382 currentParentIsContainer = true;1383 break findParent;1384 case HostPortal:1385 currentParent = parentStateNode.containerInfo;1386 currentParentIsContainer = true;1387 break findParent;1388 case FundamentalComponent:1389 if (enableFundamentalAPI) {1390 currentParent = parentStateNode.instance;1391 currentParentIsContainer = false;1392 }1393 }1394 parent = parent.return;1395 }1396 currentParentIsValid = true;1397 }1398 if (node.tag === HostComponent || node.tag === HostText) {1399 commitNestedUnmounts(1400 finishedRoot,1401 node,1402 nearestMountedAncestor,1403 renderPriorityLevel,1404 );1405 // After all the children have unmounted, it is now safe to remove the1406 // node from the tree.1407 if (currentParentIsContainer) {1408 removeChildFromContainer(1409 ((currentParent ) ),1410 (node.stateNode ),1411 );1412 } else {1413 removeChild(1414 ((currentParent ) ),1415 (node.stateNode ),1416 );1417 }1418 // Don't visit children because we already visited them.1419 } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {1420 const fundamentalNode = node.stateNode.instance;1421 commitNestedUnmounts(1422 finishedRoot,1423 node,1424 nearestMountedAncestor,1425 renderPriorityLevel,1426 );1427 // After all the children have unmounted, it is now safe to remove the1428 // node from the tree.1429 if (currentParentIsContainer) {1430 removeChildFromContainer(1431 ((currentParent ) ),1432 (fundamentalNode ),1433 );1434 } else {1435 removeChild(1436 ((currentParent ) ),1437 (fundamentalNode ),1438 );1439 }1440 } else if (1441 enableSuspenseServerRenderer &&1442 node.tag === DehydratedFragment1443 ) {1444 if (enableSuspenseCallback) {1445 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1446 if (hydrationCallbacks !== null) {1447 const onDeleted = hydrationCallbacks.onDeleted;1448 if (onDeleted) {1449 onDeleted((node.stateNode ));1450 }1451 }1452 }1453 // Delete the dehydrated suspense boundary and all of its content.1454 if (currentParentIsContainer) {1455 clearSuspenseBoundaryFromContainer(1456 ((currentParent ) ),1457 (node.stateNode ),1458 );1459 } else {1460 clearSuspenseBoundary(1461 ((currentParent ) ),1462 (node.stateNode ),1463 );1464 }1465 } else if (node.tag === HostPortal) {1466 if (node.child !== null) {1467 // When we go into a portal, it becomes the parent to remove from.1468 // We will reassign it back when we pop the portal on the way up.1469 currentParent = node.stateNode.containerInfo;1470 currentParentIsContainer = true;1471 // Visit children because portals might contain host components.1472 node.child.return = node;1473 node = node.child;1474 continue;1475 }1476 } else {1477 commitUnmount(1478 finishedRoot,1479 node,1480 nearestMountedAncestor,1481 renderPriorityLevel,1482 );1483 // Visit children because we may find more host components below.1484 if (node.child !== null) {1485 node.child.return = node;1486 node = node.child;1487 continue;1488 }1489 }1490 if (node === current) {1491 return;1492 }1493 while (node.sibling === null) {1494 if (node.return === null || node.return === current) {1495 return;1496 }1497 node = node.return;1498 if (node.tag === HostPortal) {1499 // When we go out of the portal, we need to restore the parent.1500 // Since we don't keep a stack of them, we will search for it.1501 currentParentIsValid = false;1502 }1503 }1504 node.sibling.return = node.return;1505 node = node.sibling;1506 }1507}1508function commitDeletion(1509 finishedRoot ,1510 current ,1511 nearestMountedAncestor ,1512 renderPriorityLevel ,1513) {1514 if (supportsMutation) {1515 // Recursively delete all host nodes from the parent.1516 // Detach refs and call componentWillUnmount() on the whole subtree.1517 unmountHostComponents(1518 finishedRoot,1519 current,1520 nearestMountedAncestor,1521 renderPriorityLevel,1522 );1523 } else {1524 // Detach refs and call componentWillUnmount() on the whole subtree.1525 commitNestedUnmounts(1526 finishedRoot,1527 current,1528 nearestMountedAncestor,1529 renderPriorityLevel,1530 );1531 }1532 const alternate = current.alternate;1533 detachFiberMutation(current);1534 if (alternate !== null) {1535 detachFiberMutation(alternate);1536 }1537}1538function commitWork(current , finishedWork ) {1539 if (!supportsMutation) {1540 switch (finishedWork.tag) {1541 case FunctionComponent:1542 case ForwardRef:1543 case MemoComponent:1544 case SimpleMemoComponent:1545 case Block: {1546 // Layout effects are destroyed during the mutation phase so that all1547 // destroy functions for all fibers are called before any create functions.1548 // This prevents sibling component effects from interfering with each other,1549 // e.g. a destroy function in one component should never override a ref set1550 // by a create function in another component during the same commit.1551 if (1552 enableProfilerTimer &&1553 enableProfilerCommitHooks &&1554 finishedWork.mode & ProfileMode1555 ) {1556 try {1557 startLayoutEffectTimer();1558 commitHookEffectListUnmount(1559 HookLayout | HookHasEffect,1560 finishedWork,1561 finishedWork.return,1562 );1563 } finally {1564 recordLayoutEffectDuration(finishedWork);1565 }1566 } else {1567 commitHookEffectListUnmount(1568 HookLayout | HookHasEffect,1569 finishedWork,1570 finishedWork.return,1571 );1572 }1573 return;1574 }1575 case Profiler: {1576 return;1577 }1578 case SuspenseComponent: {1579 commitSuspenseComponent(finishedWork);1580 attachSuspenseRetryListeners(finishedWork);1581 return;1582 }1583 case SuspenseListComponent: {1584 attachSuspenseRetryListeners(finishedWork);1585 return;1586 }1587 case HostRoot: {1588 if (supportsHydration) {1589 const root = finishedWork.stateNode;1590 if (root.hydrate) {1591 // We've just hydrated. No need to hydrate again.1592 root.hydrate = false;1593 commitHydratedContainer(root.containerInfo);1594 }1595 }1596 break;1597 }1598 case OffscreenComponent:1599 case LegacyHiddenComponent: {1600 return;1601 }1602 }1603 commitContainer(finishedWork);1604 return;1605 }1606 switch (finishedWork.tag) {1607 case FunctionComponent:1608 case ForwardRef:1609 case MemoComponent:1610 case SimpleMemoComponent:1611 case Block: {1612 // Layout effects are destroyed during the mutation phase so that all1613 // destroy functions for all fibers are called before any create functions.1614 // This prevents sibling component effects from interfering with each other,1615 // e.g. a destroy function in one component should never override a ref set1616 // by a create function in another component during the same commit.1617 if (1618 enableProfilerTimer &&1619 enableProfilerCommitHooks &&1620 finishedWork.mode & ProfileMode1621 ) {1622 try {1623 startLayoutEffectTimer();1624 commitHookEffectListUnmount(1625 HookLayout | HookHasEffect,1626 finishedWork,1627 finishedWork.return,1628 );1629 } finally {1630 recordLayoutEffectDuration(finishedWork);1631 }1632 } else {1633 commitHookEffectListUnmount(1634 HookLayout | HookHasEffect,1635 finishedWork,1636 finishedWork.return,1637 );1638 }1639 return;1640 }1641 case ClassComponent: {1642 return;1643 }1644 case HostComponent: {1645 const instance = finishedWork.stateNode;1646 if (instance != null) {1647 // Commit the work prepared earlier.1648 const newProps = finishedWork.memoizedProps;1649 // For hydration we reuse the update path but we treat the oldProps1650 // as the newProps. The updatePayload will contain the real change in1651 // this case.1652 const oldProps = current !== null ? current.memoizedProps : newProps;1653 const type = finishedWork.type;1654 // TODO: Type the updateQueue to be specific to host components.1655 const updatePayload = (finishedWork.updateQueue );1656 finishedWork.updateQueue = null;1657 if (updatePayload !== null) {1658 commitUpdate(1659 instance,1660 updatePayload,1661 type,1662 oldProps,1663 newProps,1664 finishedWork,1665 );1666 }1667 }1668 return;1669 }1670 case HostText: {1671 invariant(1672 finishedWork.stateNode !== null,1673 'This should have a text node initialized. This error is likely ' +1674 'caused by a bug in React. Please file an issue.',1675 );1676 const textInstance = finishedWork.stateNode;1677 const newText = finishedWork.memoizedProps;1678 // For hydration we reuse the update path but we treat the oldProps1679 // as the newProps. The updatePayload will contain the real change in1680 // this case.1681 const oldText =1682 current !== null ? current.memoizedProps : newText;1683 commitTextUpdate(textInstance, oldText, newText);1684 return;1685 }1686 case HostRoot: {1687 if (supportsHydration) {1688 const root = finishedWork.stateNode;1689 if (root.hydrate) {1690 // We've just hydrated. No need to hydrate again.1691 root.hydrate = false;1692 commitHydratedContainer(root.containerInfo);1693 }1694 }1695 return;1696 }1697 case Profiler: {1698 return;1699 }1700 case SuspenseComponent: {1701 commitSuspenseComponent(finishedWork);1702 attachSuspenseRetryListeners(finishedWork);1703 return;1704 }1705 case SuspenseListComponent: {1706 attachSuspenseRetryListeners(finishedWork);1707 return;1708 }1709 case IncompleteClassComponent: {1710 return;1711 }1712 case FundamentalComponent: {1713 if (enableFundamentalAPI) {1714 const fundamentalInstance = finishedWork.stateNode;1715 updateFundamentalComponent(fundamentalInstance);1716 return;1717 }1718 break;1719 }1720 case ScopeComponent: {1721 if (enableScopeAPI) {1722 const scopeInstance = finishedWork.stateNode;1723 prepareScopeUpdate(scopeInstance, finishedWork);1724 return;1725 }1726 break;1727 }1728 case OffscreenComponent:1729 case LegacyHiddenComponent: {1730 const newState = finishedWork.memoizedState;1731 const isHidden = newState !== null;1732 hideOrUnhideAllChildren(finishedWork, isHidden);1733 return;1734 }1735 }1736 invariant(1737 false,1738 'This unit of work tag should not have side-effects. This error is ' +1739 'likely caused by a bug in React. Please file an issue.',1740 );1741}1742function commitSuspenseComponent(finishedWork ) {1743 const newState = finishedWork.memoizedState;1744 if (newState !== null) {1745 markCommitTimeOfFallback();1746 if (supportsMutation) {1747 // Hide the Offscreen component that contains the primary children. TODO:1748 // Ideally, this effect would have been scheduled on the Offscreen fiber1749 // itself. That's how unhiding works: the Offscreen component schedules an1750 // effect on itself. However, in this case, the component didn't complete,1751 // so the fiber was never added to the effect list in the normal path. We1752 // could have appended it to the effect list in the Suspense component's1753 // second pass, but doing it this way is less complicated. This would be1754 // simpler if we got rid of the effect list and traversed the tree, like1755 // we're planning to do.1756 const primaryChildParent = (finishedWork.child );1757 hideOrUnhideAllChildren(primaryChildParent, true);1758 }1759 }1760 if (enableSuspenseCallback && newState !== null) {1761 const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;1762 if (typeof suspenseCallback === 'function') {1763 const wakeables = (finishedWork.updateQueue );1764 if (wakeables !== null) {1765 suspenseCallback(new Set(wakeables));1766 }1767 } else if (__DEV__) {1768 if (suspenseCallback !== undefined) {1769 console.error('Unexpected type for suspenseCallback.');1770 }1771 }1772 }1773}1774function commitSuspenseHydrationCallbacks(1775 finishedRoot ,1776 finishedWork ,1777) {1778 if (!supportsHydration) {1779 return;1780 }1781 const newState = finishedWork.memoizedState;1782 if (newState === null) {1783 const current = finishedWork.alternate;1784 if (current !== null) {1785 const prevState = current.memoizedState;1786 if (prevState !== null) {1787 const suspenseInstance = prevState.dehydrated;1788 if (suspenseInstance !== null) {1789 commitHydratedSuspenseInstance(suspenseInstance);1790 if (enableSuspenseCallback) {1791 const hydrationCallbacks = finishedRoot.hydrationCallbacks;1792 if (hydrationCallbacks !== null) {1793 const onHydrated = hydrationCallbacks.onHydrated;1794 if (onHydrated) {1795 onHydrated(suspenseInstance);1796 }1797 }1798 }1799 }1800 }1801 }1802 }1803}1804function attachSuspenseRetryListeners(finishedWork ) {1805 // If this boundary just timed out, then it will have a set of wakeables.1806 // For each wakeable, attach a listener so that when it resolves, React1807 // attempts to re-render the boundary in the primary (pre-timeout) state.1808 const wakeables = (finishedWork.updateQueue );1809 if (wakeables !== null) {1810 finishedWork.updateQueue = null;1811 let retryCache = finishedWork.stateNode;1812 if (retryCache === null) {1813 retryCache = finishedWork.stateNode = new PossiblyWeakSet();1814 }1815 wakeables.forEach(wakeable => {1816 // Memoize using the boundary fiber to prevent redundant listeners.1817 let retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);1818 if (!retryCache.has(wakeable)) {1819 if (enableSchedulerTracing) {1820 if (wakeable.__reactDoNotTraceInteractions !== true) {1821 retry = Schedule_tracing_wrap(retry);1822 }1823 }1824 retryCache.add(wakeable);1825 wakeable.then(retry, retry);1826 }1827 });1828 }1829}1830// This function detects when a Suspense boundary goes from visible to hidden.1831// It returns false if the boundary is already hidden.1832// TODO: Use an effect tag.1833export function isSuspenseBoundaryBeingHidden(1834 current ,1835 finishedWork ,1836) {1837 if (current !== null) {1838 const oldState = current.memoizedState;1839 if (oldState === null || oldState.dehydrated !== null) {1840 const newState = finishedWork.memoizedState;1841 return newState !== null && newState.dehydrated === null;1842 }1843 }1844 return false;1845}1846function commitResetTextContent(current ) {1847 if (!supportsMutation) {1848 return;1849 }1850 resetTextContent(current.stateNode);1851}1852function commitPassiveUnmount(finishedWork ) {1853 switch (finishedWork.tag) {1854 case FunctionComponent:1855 case ForwardRef:1856 case SimpleMemoComponent:1857 case Block: {1858 if (1859 enableProfilerTimer &&1860 enableProfilerCommitHooks &&1861 finishedWork.mode & ProfileMode1862 ) {1863 startPassiveEffectTimer();1864 commitHookEffectListUnmount(1865 HookPassive | HookHasEffect,1866 finishedWork,1867 finishedWork.return,1868 );1869 recordPassiveEffectDuration(finishedWork);1870 } else {1871 commitHookEffectListUnmount(1872 HookPassive | HookHasEffect,1873 finishedWork,1874 finishedWork.return,1875 );1876 }1877 break;1878 }1879 }1880}1881function commitPassiveUnmountInsideDeletedTree(1882 current ,1883 nearestMountedAncestor ,1884) {1885 switch (current.tag) {1886 case FunctionComponent:1887 case ForwardRef:1888 case SimpleMemoComponent:1889 case Block: {1890 if (1891 enableProfilerTimer &&1892 enableProfilerCommitHooks &&1893 current.mode & ProfileMode1894 ) {1895 startPassiveEffectTimer();1896 commitHookEffectListUnmount(1897 HookPassive,1898 current,1899 nearestMountedAncestor,1900 );1901 recordPassiveEffectDuration(current);1902 } else {1903 commitHookEffectListUnmount(1904 HookPassive,1905 current,1906 nearestMountedAncestor,1907 );1908 }1909 break;1910 }1911 }1912}1913function commitPassiveMount(1914 finishedRoot ,1915 finishedWork ,1916) {1917 switch (finishedWork.tag) {...
ReactFiberCommitWork.js
Source:ReactFiberCommitWork.js
...63 // enableProfilerCommitHooks &&64 // finishedWork.mode & ProfileMode65 // ) {66 // startPassiveEffectTimer();67 // commitHookEffectListUnmount(68 // HookPassive | HookHasEffect,69 // finishedWork,70 // finishedWork.return,71 // );72 // recordPassiveEffectDuration(finishedWork);73 // } else {74 commitHookEffectListUnmount(75 HookPassive | HookHasEffect,76 finishedWork,77 finishedWork.return78 );79 // }80 break;81 }82 }83}84function commitHookEffectListUnmount(flags, finishedWork, nearestMountedAncestor) {85 const updateQueue = finishedWork.updateQueue86 const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;87 if (lastEffect !== null) {88 const firstEffect = lastEffect.next;89 let effect = firstEffect;90 do {91 if ((effect.tag & flags) === flags) {92 // Unmount93 const destroy = effect.destroy;94 effect.destroy = undefined;95 if (destroy !== undefined) {96 safelyCallDestroy(finishedWork, nearestMountedAncestor)97 }98 }...
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!!