diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 00dc626824516..1517a183c2cf9 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -1015,30 +1015,19 @@ function commitNestedUnmounts( } function detachFiberMutation(fiber: Fiber) { - // Cut off the return pointers to disconnect it from the tree. - // Note that we can't clear child or sibling pointers yet, - // because they may be required for passive effects. - // These pointers will be cleared in a separate pass. - // Ideally, we should clear the child pointer of the parent alternate to let this + // Cut off the return pointer to disconnect it from the tree. + // This enables us to detect and warn against state updates on an unmounted component. + // It also prevents events from bubbling from within disconnected components. + // + // Ideally, we should also clear the child pointer of the parent alternate to let this // get GC:ed but we don't know which for sure which parent is the current - // one so we'll settle for GC:ing the subtree of this child. This child - // itself will be GC:ed when the parent updates the next time. - // Note: we cannot null out sibling here, otherwise it can cause issues - // with findDOMNode and how it requires the sibling field to carry out - // traversal in a later effect. See PR #16820. We now clear the sibling - // field after effects, see: detachFiberAfterEffects. - fiber.alternate = null; - fiber.dependencies = null; - fiber.firstEffect = null; - fiber.lastEffect = null; - fiber.memoizedProps = null; - fiber.memoizedState = null; - fiber.pendingProps = null; + // one so we'll settle for GC:ing the subtree of this child. + // This child itself will be GC:ed when the parent updates the next time. + // + // Note that we can't clear child or sibling pointers yet. + // They're needed for passive effects and for findDOMNode. + // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects). fiber.return = null; - fiber.stateNode = null; - if (__DEV__) { - fiber._debugOwner = null; - } } function emptyPortalContainer(current: Fiber) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 04f57c4ac992b..7142d1b36f605 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -4060,7 +4060,21 @@ export function act(callback: () => Thenable): Thenable { } function detachFiberAfterEffects(fiber: Fiber): void { + // Null out fields to improve GC for references that may be lingering (e.g. DevTools). + // Note that we already cleared the return pointer in detachFiberMutation(). + fiber.alternate = null; fiber.child = null; + fiber.dependencies = null; + fiber.firstEffect = null; + fiber.lastEffect = null; + fiber.memoizedProps = null; + fiber.memoizedState = null; + fiber.pendingProps = null; fiber.sibling = null; + fiber.stateNode = null; fiber.updateQueue = null; + + if (__DEV__) { + fiber._debugOwner = null; + } }