From 40eaa22d9af685c239f9d8d42b454d031791e76d Mon Sep 17 00:00:00 2001 From: Luna Ruan Date: Sat, 19 Feb 2022 10:25:27 -0500 Subject: [PATCH] Remove dependency on Offscreen Fiber updateQueue for React Cache (#23229) We need to use the Offscreen Fiber's update queue for interaction tracing. This PR removes the optimization that React Cache uses to not need to push and pop the cache in special circumstances and defaults to always pushing and popping the cache as long as there was a previous cache. --- .../src/ReactFiberBeginWork.new.js | 70 ++++++++++--------- .../src/ReactFiberBeginWork.old.js | 70 ++++++++++--------- .../src/ReactFiberCacheComponent.new.js | 26 +++---- .../src/ReactFiberCacheComponent.old.js | 26 +++---- .../src/ReactFiberCompleteWork.new.js | 21 +++--- .../src/ReactFiberCompleteWork.old.js | 21 +++--- .../src/ReactFiberUnwindWork.new.js | 20 ++++-- .../src/ReactFiberUnwindWork.old.js | 20 ++++-- .../src/ReactFiberWorkLoop.new.js | 11 ++- .../src/ReactFiberWorkLoop.old.js | 11 ++- 10 files changed, 150 insertions(+), 146 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 102ee33376616..9b8de1df80183 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -234,7 +234,7 @@ import { pushRootCachePool, CacheContext, getSuspendedCachePool, - restoreSpawnedCachePool, + pushSpawnedCachePool, getOffscreenDeferredCachePool, } from './ReactFiberCacheComponent.new'; import {createCapturedValue} from './ReactCapturedValue'; @@ -635,11 +635,6 @@ function updateOffscreenComponent( const prevState: OffscreenState | null = current !== null ? current.memoizedState : null; - // If this is not null, this is a cache pool that was carried over from the - // previous render. We will push this to the cache pool context so that we can - // resume in-flight requests. - let spawnedCachePool: SpawnedCachePool | null = null; - if ( nextProps.mode === 'hidden' || nextProps.mode === 'unstable-defer-without-hiding' @@ -652,8 +647,16 @@ function updateOffscreenComponent( cachePool: null, }; workInProgress.memoizedState = nextState; + if (enableCache) { + // push the cache pool even though we're going to bail out + // because otherwise there'd be a context mismatch + if (current !== null) { + pushSpawnedCachePool(workInProgress, null); + } + } pushRenderLanes(workInProgress, renderLanes); } else if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) { + let spawnedCachePool: SpawnedCachePool | null = null; // We're hidden, and we're not rendering at Offscreen. We will bail out // and resume this tree later. let nextBaseLanes; @@ -663,9 +666,6 @@ function updateOffscreenComponent( if (enableCache) { // Save the cache pool so we can resume later. spawnedCachePool = getOffscreenDeferredCachePool(); - // We don't need to push to the cache pool because we're about to - // bail out. There won't be a context mismatch because we only pop - // the cache pool if `updateQueue` is non-null. } } else { nextBaseLanes = renderLanes; @@ -681,6 +681,14 @@ function updateOffscreenComponent( }; workInProgress.memoizedState = nextState; workInProgress.updateQueue = null; + if (enableCache) { + // push the cache pool even though we're going to bail out + // because otherwise there'd be a context mismatch + if (current !== null) { + pushSpawnedCachePool(workInProgress, null); + } + } + // We're about to bail out, but we need to push this to the stack anyway // to avoid a push/pop misalignment. pushRenderLanes(workInProgress, nextBaseLanes); @@ -701,19 +709,6 @@ function updateOffscreenComponent( // This is the second render. The surrounding visible content has already // committed. Now we resume rendering the hidden tree. - if (enableCache && prevState !== null) { - // If the render that spawned this one accessed the cache pool, resume - // using the same cache. Unless the parent changed, since that means - // there was a refresh. - const prevCachePool = prevState.cachePool; - if (prevCachePool !== null) { - spawnedCachePool = restoreSpawnedCachePool( - workInProgress, - prevCachePool, - ); - } - } - // Rendering at offscreen, so we can clear the base lanes. const nextState: OffscreenState = { baseLanes: NoLanes, @@ -723,6 +718,14 @@ function updateOffscreenComponent( // Push the lanes that were skipped when we bailed out. const subtreeRenderLanes = prevState !== null ? prevState.baseLanes : renderLanes; + if (enableCache && current !== null) { + // If the render that spawned this one accessed the cache pool, resume + // using the same cache. Unless the parent changed, since that means + // there was a refresh. + const prevCachePool = prevState !== null ? prevState.cachePool : null; + pushSpawnedCachePool(workInProgress, prevCachePool); + } + pushRenderLanes(workInProgress, subtreeRenderLanes); } } else { @@ -738,12 +741,7 @@ function updateOffscreenComponent( // using the same cache. Unless the parent changed, since that means // there was a refresh. const prevCachePool = prevState.cachePool; - if (prevCachePool !== null) { - spawnedCachePool = restoreSpawnedCachePool( - workInProgress, - prevCachePool, - ); - } + pushSpawnedCachePool(workInProgress, prevCachePool); } // Since we're not hidden anymore, reset the state @@ -753,16 +751,19 @@ function updateOffscreenComponent( // special to do. Need to push to the stack regardless, though, to avoid // a push/pop misalignment. subtreeRenderLanes = renderLanes; + + if (enableCache) { + // If the render that spawned this one accessed the cache pool, resume + // using the same cache. Unless the parent changed, since that means + // there was a refresh. + if (current !== null) { + pushSpawnedCachePool(workInProgress, null); + } + } } pushRenderLanes(workInProgress, subtreeRenderLanes); } - if (enableCache) { - // If we have a cache pool from a previous render attempt, then this will be - // non-null. We use this to infer whether to push/pop the cache context. - workInProgress.updateQueue = spawnedCachePool; - } - if (enablePersistentOffscreenHostContainer && supportsPersistence) { // In persistent mode, the offscreen children are wrapped in a host node. // TODO: Optimize this to use the OffscreenComponent fiber instead of @@ -2072,6 +2073,7 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { const nextPrimaryChildren = nextProps.children; const nextFallbackChildren = nextProps.fallback; + if (showFallback) { const fallbackFragment = mountSuspenseFallbackChildren( workInProgress, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 5876cb3eee702..457088598e51b 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -234,7 +234,7 @@ import { pushRootCachePool, CacheContext, getSuspendedCachePool, - restoreSpawnedCachePool, + pushSpawnedCachePool, getOffscreenDeferredCachePool, } from './ReactFiberCacheComponent.old'; import {createCapturedValue} from './ReactCapturedValue'; @@ -635,11 +635,6 @@ function updateOffscreenComponent( const prevState: OffscreenState | null = current !== null ? current.memoizedState : null; - // If this is not null, this is a cache pool that was carried over from the - // previous render. We will push this to the cache pool context so that we can - // resume in-flight requests. - let spawnedCachePool: SpawnedCachePool | null = null; - if ( nextProps.mode === 'hidden' || nextProps.mode === 'unstable-defer-without-hiding' @@ -652,8 +647,16 @@ function updateOffscreenComponent( cachePool: null, }; workInProgress.memoizedState = nextState; + if (enableCache) { + // push the cache pool even though we're going to bail out + // because otherwise there'd be a context mismatch + if (current !== null) { + pushSpawnedCachePool(workInProgress, null); + } + } pushRenderLanes(workInProgress, renderLanes); } else if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) { + let spawnedCachePool: SpawnedCachePool | null = null; // We're hidden, and we're not rendering at Offscreen. We will bail out // and resume this tree later. let nextBaseLanes; @@ -663,9 +666,6 @@ function updateOffscreenComponent( if (enableCache) { // Save the cache pool so we can resume later. spawnedCachePool = getOffscreenDeferredCachePool(); - // We don't need to push to the cache pool because we're about to - // bail out. There won't be a context mismatch because we only pop - // the cache pool if `updateQueue` is non-null. } } else { nextBaseLanes = renderLanes; @@ -681,6 +681,14 @@ function updateOffscreenComponent( }; workInProgress.memoizedState = nextState; workInProgress.updateQueue = null; + if (enableCache) { + // push the cache pool even though we're going to bail out + // because otherwise there'd be a context mismatch + if (current !== null) { + pushSpawnedCachePool(workInProgress, null); + } + } + // We're about to bail out, but we need to push this to the stack anyway // to avoid a push/pop misalignment. pushRenderLanes(workInProgress, nextBaseLanes); @@ -701,19 +709,6 @@ function updateOffscreenComponent( // This is the second render. The surrounding visible content has already // committed. Now we resume rendering the hidden tree. - if (enableCache && prevState !== null) { - // If the render that spawned this one accessed the cache pool, resume - // using the same cache. Unless the parent changed, since that means - // there was a refresh. - const prevCachePool = prevState.cachePool; - if (prevCachePool !== null) { - spawnedCachePool = restoreSpawnedCachePool( - workInProgress, - prevCachePool, - ); - } - } - // Rendering at offscreen, so we can clear the base lanes. const nextState: OffscreenState = { baseLanes: NoLanes, @@ -723,6 +718,14 @@ function updateOffscreenComponent( // Push the lanes that were skipped when we bailed out. const subtreeRenderLanes = prevState !== null ? prevState.baseLanes : renderLanes; + if (enableCache && current !== null) { + // If the render that spawned this one accessed the cache pool, resume + // using the same cache. Unless the parent changed, since that means + // there was a refresh. + const prevCachePool = prevState !== null ? prevState.cachePool : null; + pushSpawnedCachePool(workInProgress, prevCachePool); + } + pushRenderLanes(workInProgress, subtreeRenderLanes); } } else { @@ -738,12 +741,7 @@ function updateOffscreenComponent( // using the same cache. Unless the parent changed, since that means // there was a refresh. const prevCachePool = prevState.cachePool; - if (prevCachePool !== null) { - spawnedCachePool = restoreSpawnedCachePool( - workInProgress, - prevCachePool, - ); - } + pushSpawnedCachePool(workInProgress, prevCachePool); } // Since we're not hidden anymore, reset the state @@ -753,16 +751,19 @@ function updateOffscreenComponent( // special to do. Need to push to the stack regardless, though, to avoid // a push/pop misalignment. subtreeRenderLanes = renderLanes; + + if (enableCache) { + // If the render that spawned this one accessed the cache pool, resume + // using the same cache. Unless the parent changed, since that means + // there was a refresh. + if (current !== null) { + pushSpawnedCachePool(workInProgress, null); + } + } } pushRenderLanes(workInProgress, subtreeRenderLanes); } - if (enableCache) { - // If we have a cache pool from a previous render attempt, then this will be - // non-null. We use this to infer whether to push/pop the cache context. - workInProgress.updateQueue = spawnedCachePool; - } - if (enablePersistentOffscreenHostContainer && supportsPersistence) { // In persistent mode, the offscreen children are wrapped in a host node. // TODO: Optimize this to use the OffscreenComponent fiber instead of @@ -2072,6 +2073,7 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { const nextPrimaryChildren = nextProps.children; const nextFallbackChildren = nextProps.fallback; + if (showFallback) { const fallbackFragment = mountSuspenseFallbackChildren( workInProgress, diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js index cf851af6e3de3..3d1db8124b692 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js @@ -198,29 +198,18 @@ export function popRootCachePool(root: FiberRoot, renderLanes: Lanes) { // code organization purposes in case that changes. } -export function restoreSpawnedCachePool( +export function pushSpawnedCachePool( offscreenWorkInProgress: Fiber, - prevCachePool: SpawnedCachePool, -): SpawnedCachePool | null { + prevCachePool: SpawnedCachePool | null, +): void { if (!enableCache) { - return (null: any); + return; } - const nextParentCache = isPrimaryRenderer - ? CacheContext._currentValue - : CacheContext._currentValue2; - if (nextParentCache !== prevCachePool.parent) { - // There was a refresh. Don't bother restoring anything since the refresh - // will override it. - return null; + + if (prevCachePool === null) { + push(resumedCache, resumedCache.current, offscreenWorkInProgress); } else { - // No refresh. Resume with the previous cache. New Cache boundaries in the - // subtree use this one instead of requesting a fresh one (see - // peekCacheFromPool). push(resumedCache, prevCachePool.pool, offscreenWorkInProgress); - - // Return the cache pool to signal that we did in fact push it. We will - // assign this to the field on the fiber so we know to pop the context. - return prevCachePool; } } @@ -228,6 +217,7 @@ export function popCachePool(workInProgress: Fiber) { if (!enableCache) { return; } + pop(resumedCache, workInProgress); } diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js index 692dba781302e..74dc9a449a507 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js @@ -198,29 +198,18 @@ export function popRootCachePool(root: FiberRoot, renderLanes: Lanes) { // code organization purposes in case that changes. } -export function restoreSpawnedCachePool( +export function pushSpawnedCachePool( offscreenWorkInProgress: Fiber, - prevCachePool: SpawnedCachePool, -): SpawnedCachePool | null { + prevCachePool: SpawnedCachePool | null, +): void { if (!enableCache) { - return (null: any); + return; } - const nextParentCache = isPrimaryRenderer - ? CacheContext._currentValue - : CacheContext._currentValue2; - if (nextParentCache !== prevCachePool.parent) { - // There was a refresh. Don't bother restoring anything since the refresh - // will override it. - return null; + + if (prevCachePool === null) { + push(resumedCache, resumedCache.current, offscreenWorkInProgress); } else { - // No refresh. Resume with the previous cache. New Cache boundaries in the - // subtree use this one instead of requesting a fresh one (see - // peekCacheFromPool). push(resumedCache, prevCachePool.pool, offscreenWorkInProgress); - - // Return the cache pool to signal that we did in fact push it. We will - // assign this to the field on the fiber so we know to pop the context. - return prevCachePool; } } @@ -228,6 +217,7 @@ export function popCachePool(workInProgress: Fiber) { if (!enableCache) { return; } + pop(resumedCache, workInProgress); } diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index 39f724c76df92..bb01e2b8a2a26 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -28,7 +28,7 @@ import type { } from './ReactFiberSuspenseComponent.new'; import type {SuspenseContext} from './ReactFiberSuspenseContext.new'; import type {OffscreenState} from './ReactFiberOffscreenComponent'; -import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new'; +import type {Cache} from './ReactFiberCacheComponent.new'; import { enableClientRenderFallbackOnHydrationMismatch, enableSuspenseAvoidThisFallback, @@ -865,8 +865,8 @@ function completeWork( popRootCachePool(fiberRoot, renderLanes); let previousCache: Cache | null = null; - if (workInProgress.alternate !== null) { - previousCache = workInProgress.alternate.memoizedState.cache; + if (current !== null) { + previousCache = current.memoizedState.cache; } const cache: Cache = workInProgress.memoizedState.cache; if (cache !== previousCache) { @@ -1533,11 +1533,11 @@ function completeWork( if (enableCache) { let previousCache: Cache | null = null; if ( - workInProgress.alternate !== null && - workInProgress.alternate.memoizedState !== null && - workInProgress.alternate.memoizedState.cachePool !== null + current !== null && + current.memoizedState !== null && + current.memoizedState.cachePool !== null ) { - previousCache = workInProgress.alternate.memoizedState.cachePool.pool; + previousCache = current.memoizedState.cachePool.pool; } let cache: Cache | null = null; if ( @@ -1550,8 +1550,7 @@ function completeWork( // Run passive effects to retain/release the cache. workInProgress.flags |= Passive; } - const spawnedCachePool: SpawnedCachePool | null = (workInProgress.updateQueue: any); - if (spawnedCachePool !== null) { + if (current !== null) { popCachePool(workInProgress); } } @@ -1561,8 +1560,8 @@ function completeWork( case CacheComponent: { if (enableCache) { let previousCache: Cache | null = null; - if (workInProgress.alternate !== null) { - previousCache = workInProgress.alternate.memoizedState.cache; + if (current !== null) { + previousCache = current.memoizedState.cache; } const cache: Cache = workInProgress.memoizedState.cache; if (cache !== previousCache) { diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js index c7baf36d16c47..0d9de679a0f43 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js @@ -28,7 +28,7 @@ import type { } from './ReactFiberSuspenseComponent.old'; import type {SuspenseContext} from './ReactFiberSuspenseContext.old'; import type {OffscreenState} from './ReactFiberOffscreenComponent'; -import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old'; +import type {Cache} from './ReactFiberCacheComponent.old'; import { enableClientRenderFallbackOnHydrationMismatch, enableSuspenseAvoidThisFallback, @@ -865,8 +865,8 @@ function completeWork( popRootCachePool(fiberRoot, renderLanes); let previousCache: Cache | null = null; - if (workInProgress.alternate !== null) { - previousCache = workInProgress.alternate.memoizedState.cache; + if (current !== null) { + previousCache = current.memoizedState.cache; } const cache: Cache = workInProgress.memoizedState.cache; if (cache !== previousCache) { @@ -1533,11 +1533,11 @@ function completeWork( if (enableCache) { let previousCache: Cache | null = null; if ( - workInProgress.alternate !== null && - workInProgress.alternate.memoizedState !== null && - workInProgress.alternate.memoizedState.cachePool !== null + current !== null && + current.memoizedState !== null && + current.memoizedState.cachePool !== null ) { - previousCache = workInProgress.alternate.memoizedState.cachePool.pool; + previousCache = current.memoizedState.cachePool.pool; } let cache: Cache | null = null; if ( @@ -1550,8 +1550,7 @@ function completeWork( // Run passive effects to retain/release the cache. workInProgress.flags |= Passive; } - const spawnedCachePool: SpawnedCachePool | null = (workInProgress.updateQueue: any); - if (spawnedCachePool !== null) { + if (current !== null) { popCachePool(workInProgress); } } @@ -1561,8 +1560,8 @@ function completeWork( case CacheComponent: { if (enableCache) { let previousCache: Cache | null = null; - if (workInProgress.alternate !== null) { - previousCache = workInProgress.alternate.memoizedState.cache; + if (current !== null) { + previousCache = current.memoizedState.cache; } const cache: Cache = workInProgress.memoizedState.cache; if (cache !== previousCache) { diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js index a43c021d987e5..aa1b391b0367f 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js @@ -11,7 +11,7 @@ import type {ReactContext} from 'shared/ReactTypes'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane.new'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; -import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new'; +import type {Cache} from './ReactFiberCacheComponent.new'; import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new'; import { @@ -52,7 +52,11 @@ import { import {transferActualDuration} from './ReactProfilerTimer.new'; import {popTreeContext} from './ReactFiberTreeContext.new'; -function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { +function unwindWork( + current: Fiber | null, + workInProgress: Fiber, + renderLanes: Lanes, +) { // Note: This intentionally doesn't check if we're hydrating because comparing // to the current tree provider fiber is just as fast and less error-prone. // Ideally we would have a special version of the work loop only @@ -153,8 +157,7 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { case LegacyHiddenComponent: popRenderLanes(workInProgress); if (enableCache) { - const spawnedCachePool: SpawnedCachePool | null = (workInProgress.updateQueue: any); - if (spawnedCachePool !== null) { + if (current !== null) { popCachePool(workInProgress); } } @@ -170,7 +173,11 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { } } -function unwindInterruptedWork(interruptedWork: Fiber, renderLanes: Lanes) { +function unwindInterruptedWork( + current: Fiber | null, + interruptedWork: Fiber, + renderLanes: Lanes, +) { // Note: This intentionally doesn't check if we're hydrating because comparing // to the current tree provider fiber is just as fast and less error-prone. // Ideally we would have a special version of the work loop only @@ -218,8 +225,7 @@ function unwindInterruptedWork(interruptedWork: Fiber, renderLanes: Lanes) { case LegacyHiddenComponent: popRenderLanes(interruptedWork); if (enableCache) { - const spawnedCachePool: SpawnedCachePool | null = (interruptedWork.updateQueue: any); - if (spawnedCachePool !== null) { + if (current !== null) { popCachePool(interruptedWork); } } diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js index e0cf7cc2f0fcc..cb5df61782bc3 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js @@ -11,7 +11,7 @@ import type {ReactContext} from 'shared/ReactTypes'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane.old'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; -import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old'; +import type {Cache} from './ReactFiberCacheComponent.old'; import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old'; import { @@ -52,7 +52,11 @@ import { import {transferActualDuration} from './ReactProfilerTimer.old'; import {popTreeContext} from './ReactFiberTreeContext.old'; -function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { +function unwindWork( + current: Fiber | null, + workInProgress: Fiber, + renderLanes: Lanes, +) { // Note: This intentionally doesn't check if we're hydrating because comparing // to the current tree provider fiber is just as fast and less error-prone. // Ideally we would have a special version of the work loop only @@ -153,8 +157,7 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { case LegacyHiddenComponent: popRenderLanes(workInProgress); if (enableCache) { - const spawnedCachePool: SpawnedCachePool | null = (workInProgress.updateQueue: any); - if (spawnedCachePool !== null) { + if (current !== null) { popCachePool(workInProgress); } } @@ -170,7 +173,11 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { } } -function unwindInterruptedWork(interruptedWork: Fiber, renderLanes: Lanes) { +function unwindInterruptedWork( + current: Fiber | null, + interruptedWork: Fiber, + renderLanes: Lanes, +) { // Note: This intentionally doesn't check if we're hydrating because comparing // to the current tree provider fiber is just as fast and less error-prone. // Ideally we would have a special version of the work loop only @@ -218,8 +225,7 @@ function unwindInterruptedWork(interruptedWork: Fiber, renderLanes: Lanes) { case LegacyHiddenComponent: popRenderLanes(interruptedWork); if (enableCache) { - const spawnedCachePool: SpawnedCachePool | null = (interruptedWork.updateQueue: any); - if (spawnedCachePool !== null) { + if (current !== null) { popCachePool(interruptedWork); } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index c69455bde3708..cf0fb63f1936a 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1405,7 +1405,12 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) { if (workInProgress !== null) { let interruptedWork = workInProgress.return; while (interruptedWork !== null) { - unwindInterruptedWork(interruptedWork, workInProgressRootRenderLanes); + const current = interruptedWork.alternate; + unwindInterruptedWork( + current, + interruptedWork, + workInProgressRootRenderLanes, + ); interruptedWork = interruptedWork.return; } } @@ -1829,7 +1834,7 @@ function completeUnitOfWork(unitOfWork: Fiber): void { // This fiber did not complete because something threw. Pop values off // the stack without entering the complete phase. If this is a boundary, // capture values if possible. - const next = unwindWork(completedWork, subtreeRenderLanes); + const next = unwindWork(current, completedWork, subtreeRenderLanes); // Because this fiber did not complete, don't reset its lanes. @@ -2814,7 +2819,7 @@ if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) { // same fiber again. // Unwind the failed stack frame - unwindInterruptedWork(unitOfWork, workInProgressRootRenderLanes); + unwindInterruptedWork(current, unitOfWork, workInProgressRootRenderLanes); // Restore the original properties of the fiber. assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index ec9e54c1cab1e..b823ed9133130 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1405,7 +1405,12 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) { if (workInProgress !== null) { let interruptedWork = workInProgress.return; while (interruptedWork !== null) { - unwindInterruptedWork(interruptedWork, workInProgressRootRenderLanes); + const current = interruptedWork.alternate; + unwindInterruptedWork( + current, + interruptedWork, + workInProgressRootRenderLanes, + ); interruptedWork = interruptedWork.return; } } @@ -1829,7 +1834,7 @@ function completeUnitOfWork(unitOfWork: Fiber): void { // This fiber did not complete because something threw. Pop values off // the stack without entering the complete phase. If this is a boundary, // capture values if possible. - const next = unwindWork(completedWork, subtreeRenderLanes); + const next = unwindWork(current, completedWork, subtreeRenderLanes); // Because this fiber did not complete, don't reset its lanes. @@ -2814,7 +2819,7 @@ if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) { // same fiber again. // Unwind the failed stack frame - unwindInterruptedWork(unitOfWork, workInProgressRootRenderLanes); + unwindInterruptedWork(current, unitOfWork, workInProgressRootRenderLanes); // Restore the original properties of the fiber. assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);