diff --git a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js index 6b1ecbde7f018..33e3bbe441abb 100644 --- a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js +++ b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js @@ -32,6 +32,7 @@ import { import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; import {HostRoot, OffscreenComponent} from './ReactWorkTags'; import {OffscreenVisible} from './ReactFiberOffscreenComponent'; +import {getWorkInProgressRoot} from './ReactFiberWorkLoop.new'; export type ConcurrentUpdate = { next: ConcurrentUpdate, @@ -139,6 +140,18 @@ export function enqueueConcurrentHookUpdateAndEagerlyBailout( const concurrentQueue: ConcurrentQueue = (queue: any); const concurrentUpdate: ConcurrentUpdate = (update: any); enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane); + + // Usually we can rely on the upcoming render phase to process the concurrent + // queue. However, since this is a bail out, we're not scheduling any work + // here. So the update we just queued will leak until something else happens + // to schedule work (if ever). + // + // Check if we're currently in the middle of rendering a tree, and if not, + // process the queue immediately to prevent a leak. + const isConcurrentlyRendering = getWorkInProgressRoot() !== null; + if (!isConcurrentlyRendering) { + finishQueueingConcurrentUpdates(); + } } export function enqueueConcurrentClassUpdate( diff --git a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js index 798506fd9b5ea..0cd158010bbfb 100644 --- a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js +++ b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js @@ -32,6 +32,7 @@ import { import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; import {HostRoot, OffscreenComponent} from './ReactWorkTags'; import {OffscreenVisible} from './ReactFiberOffscreenComponent'; +import {getWorkInProgressRoot} from './ReactFiberWorkLoop.old'; export type ConcurrentUpdate = { next: ConcurrentUpdate, @@ -139,6 +140,18 @@ export function enqueueConcurrentHookUpdateAndEagerlyBailout( const concurrentQueue: ConcurrentQueue = (queue: any); const concurrentUpdate: ConcurrentUpdate = (update: any); enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane); + + // Usually we can rely on the upcoming render phase to process the concurrent + // queue. However, since this is a bail out, we're not scheduling any work + // here. So the update we just queued will leak until something else happens + // to schedule work (if ever). + // + // Check if we're currently in the middle of rendering a tree, and if not, + // process the queue immediately to prevent a leak. + const isConcurrentlyRendering = getWorkInProgressRoot() !== null; + if (!isConcurrentlyRendering) { + finishQueueingConcurrentUpdates(); + } } export function enqueueConcurrentClassUpdate( diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 586fcda415b9e..0e4564538efba 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1912,6 +1912,9 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) { workInProgressRoot = null; workInProgressRootRenderLanes = NoLanes; + // It's safe to process the queue now that the render phase is complete. + finishQueueingConcurrentUpdates(); + return workInProgressRootExitStatus; } @@ -2017,6 +2020,9 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { workInProgressRoot = null; workInProgressRootRenderLanes = NoLanes; + // It's safe to process the queue now that the render phase is complete. + finishQueueingConcurrentUpdates(); + // Return the final exit status. return workInProgressRootExitStatus; } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 07ec677d0aaa2..496d3f91d5724 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1912,6 +1912,9 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) { workInProgressRoot = null; workInProgressRootRenderLanes = NoLanes; + // It's safe to process the queue now that the render phase is complete. + finishQueueingConcurrentUpdates(); + return workInProgressRootExitStatus; } @@ -2017,6 +2020,9 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { workInProgressRoot = null; workInProgressRootRenderLanes = NoLanes; + // It's safe to process the queue now that the render phase is complete. + finishQueueingConcurrentUpdates(); + // Return the final exit status. return workInProgressRootExitStatus; }