How to use commitHookEffectListUnmount method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactFiberCommitWork.old.js

Source:ReactFiberCommitWork.old.js Github

copy

Full Screen

...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,...

Full Screen

Full Screen

ReactFiberCommitWork.new.js

Source:ReactFiberCommitWork.new.js Github

copy

Full Screen

...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) {...

Full Screen

Full Screen

ReactFiberCommitWork.js

Source:ReactFiberCommitWork.js Github

copy

Full Screen

...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 }...

Full Screen

Full Screen

Playwright tutorial

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.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful