diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js index d1bb0429e7c48..3125bc060413a 100644 --- a/packages/react-devtools-shared/src/backend/fiber/renderer.js +++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js @@ -1148,8 +1148,9 @@ export function attach( // Recursively unmount all roots. hook.getFiberRoots(rendererID).forEach(root => { - currentRootID = getFiberInstanceThrows(root.current).id; - unmountFiberRecursively(root.current); + const rootInstance = getFiberInstanceThrows(root.current); + currentRootID = rootInstance.id; + unmountInstanceRecursively(rootInstance); flushPendingEvents(root); currentRootID = -1; }); @@ -2183,7 +2184,8 @@ export function attach( return fiberInstance; } - function recordUnmount(fiber: Fiber): null | FiberInstance { + function recordUnmount(fiberInstance: FiberInstance): void { + const fiber = fiberInstance.data; if (__DEBUG__) { debug('recordUnmount()', fiber, null); } @@ -2200,25 +2202,13 @@ export function attach( } } - const fiberInstance = getFiberInstanceUnsafe(fiber); - if (fiberInstance === null) { - // If we've never seen this Fiber, it might be inside of a legacy render Suspense fragment (so the store is not even aware of it). - // In that case we can just ignore it or it will cause errors later on. - // One example of this is a Lazy component that never resolves before being unmounted. - // - // This also might indicate a Fast Refresh force-remount scenario. - // - // TODO: This is fragile and can obscure actual bugs. - return null; - } - const id = fiberInstance.id; const isRoot = fiber.tag === HostRoot; if (isRoot) { // Roots must be removed only after all children have been removed. // So we track it separately. pendingUnmountedRootID = id; - } else if (!shouldFilterFiber(fiber)) { + } else { // To maintain child-first ordering, // we'll push it into one of these queues, // and later arrange them in the correct order. @@ -2232,7 +2222,6 @@ export function attach( idToRootMap.delete(id); idToTreeBaseDurationMap.delete(id); } - return fiberInstance; } // Running state of the remaining children from the previous version of this parent that @@ -2308,10 +2297,7 @@ export function attach( function unmountRemainingChildren() { let child = remainingReconcilingChildren; while (child !== null) { - if (child.kind === FIBER_INSTANCE) { - unmountFiberRecursively(child.data); - } - removeChild(child); + unmountInstanceRecursively(child); child = remainingReconcilingChildren; } } @@ -2434,69 +2420,33 @@ export function attach( // We use this to simulate unmounting for Suspense trees // when we switch from primary to fallback, or deleting a subtree. - function unmountFiberRecursively(fiber: Fiber) { + function unmountInstanceRecursively(instance: DevToolsInstance) { if (__DEBUG__) { - debug('unmountFiberRecursively()', fiber, null); + if (instance.kind === FIBER_INSTANCE) { + debug('unmountInstanceRecursively()', instance.data, null); + } } - let fiberInstance = null; - - const shouldIncludeInTree = !shouldFilterFiber(fiber); const stashedParent = reconcilingParent; const stashedPrevious = previouslyReconciledSibling; const stashedRemaining = remainingReconcilingChildren; - if (shouldIncludeInTree) { - fiberInstance = getFiberInstanceThrows(fiber); - // Push a new DevTools instance parent while reconciling this subtree. - reconcilingParent = fiberInstance; - previouslyReconciledSibling = null; - // Move all the children of this instance to the remaining set. - // We'll move them back one by one, and anything that remains is deleted. - remainingReconcilingChildren = fiberInstance.firstChild; - fiberInstance.firstChild = null; - } + // Push a new DevTools instance parent while reconciling this subtree. + reconcilingParent = instance; + previouslyReconciledSibling = null; + // Move all the children of this instance to the remaining set. + remainingReconcilingChildren = instance.firstChild; try { - // We might meet a nested Suspense on our way. - const isTimedOutSuspense = - fiber.tag === SuspenseComponent && fiber.memoizedState !== null; - - if (fiber.tag === HostHoistable) { - releaseHostResource(fiber, fiber.memoizedState); - } - - let child = fiber.child; - if (isTimedOutSuspense) { - // If it's showing fallback tree, let's traverse it instead. - const primaryChildFragment = fiber.child; - const fallbackChildFragment = primaryChildFragment - ? primaryChildFragment.sibling - : null; - // Skip over to the real Fiber child. - child = fallbackChildFragment ? fallbackChildFragment.child : null; - } - - unmountChildrenRecursively(child); + // Unmount the remaining set. + unmountRemainingChildren(); } finally { - if (shouldIncludeInTree) { - reconcilingParent = stashedParent; - previouslyReconciledSibling = stashedPrevious; - remainingReconcilingChildren = stashedRemaining; - } - } - if (fiberInstance !== null) { - recordUnmount(fiber); - removeChild(fiberInstance); + reconcilingParent = stashedParent; + previouslyReconciledSibling = stashedPrevious; + remainingReconcilingChildren = stashedRemaining; } - } - - function unmountChildrenRecursively(firstChild: null | Fiber) { - let child: null | Fiber = firstChild; - while (child !== null) { - // Record simulated unmounts children-first. - // We skip nodes without return because those are real unmounts. - unmountFiberRecursively(child); - child = child.sibling; + if (instance.kind === FIBER_INSTANCE) { + recordUnmount(instance); } + removeChild(instance); } function recordProfilingDurations(fiber: Fiber) { @@ -2843,7 +2793,8 @@ export function attach( } else if (!prevDidTimeout && nextDidTimeOut) { // Primary -> Fallback: // 1. Hide primary set - unmountChildrenRecursively(prevFiber.child); + // We simply don't re-add the fallback children and let + // unmountRemainingChildren() handle it. // 2. Mount fallback set const nextFiberChild = nextFiber.child; const nextFallbackChildSet = nextFiberChild @@ -3053,19 +3004,19 @@ export function attach( const current = root.current; const alternate = current.alternate; - const existingRoot = + let rootInstance = fiberToFiberInstanceMap.get(current) || (alternate && fiberToFiberInstanceMap.get(alternate)); - if (!existingRoot) { - const newRoot = createFiberInstance(current); - idToDevToolsInstanceMap.set(newRoot.id, newRoot); - fiberToFiberInstanceMap.set(current, newRoot); + if (!rootInstance) { + rootInstance = createFiberInstance(current); + idToDevToolsInstanceMap.set(rootInstance.id, rootInstance); + fiberToFiberInstanceMap.set(current, rootInstance); if (alternate) { - fiberToFiberInstanceMap.set(alternate, newRoot); + fiberToFiberInstanceMap.set(alternate, rootInstance); } - currentRootID = newRoot.id; + currentRootID = rootInstance.id; } else { - currentRootID = existingRoot.id; + currentRootID = rootInstance.id; } // Before the traversals, remember to start tracking @@ -3123,7 +3074,7 @@ export function attach( } else if (wasMounted && !isMounted) { // Unmount an existing root. removeRootPseudoKey(currentRootID); - unmountFiberRecursively(alternate); + unmountInstanceRecursively(rootInstance); } } else { // Mount a new root.