diff --git a/packages/react-reconciler/src/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.js index 905eda36a93e8..c110b29c250ef 100644 --- a/packages/react-reconciler/src/ReactFiberLane.js +++ b/packages/react-reconciler/src/ReactFiberLane.js @@ -423,7 +423,9 @@ export function markStarvedLanesAsExpired( // We exclude retry lanes because those must always be time sliced, in order // to unwrap uncached promises. // TODO: Write a test for this - let lanes = pendingLanes & ~RetryLanes; + let lanes = enableRetryLaneExpiration + ? pendingLanes + : pendingLanes & ~RetryLanes; while (lanes > 0) { const index = pickArbitraryLaneIndex(lanes); const lane = 1 << index; diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js index d93c8bcf606ab..77aae6ee53d9b 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js @@ -5,6 +5,7 @@ let Scheduler; let act; let waitFor; let waitForAll; +let waitForMicrotasks; let assertLog; let waitForPaint; let Suspense; @@ -29,6 +30,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { waitFor = InternalTestUtils.waitFor; waitForAll = InternalTestUtils.waitForAll; waitForPaint = InternalTestUtils.waitForPaint; + waitForMicrotasks = InternalTestUtils.waitForMicrotasks; assertLog = InternalTestUtils.assertLog; getCacheForType = React.unstable_getCacheForType; @@ -4008,4 +4010,74 @@ describe('ReactSuspenseWithNoopRenderer', () => { ); }, ); + + // @gate enableLegacyCache && enableRetryLaneExpiration + it('recurring updates in siblings should not block expensive content in suspense boundary from committing', async () => { + const {useState} = React; + + let setText; + function UpdatingText() { + const [text, _setText] = useState('1'); + setText = _setText; + return ; + } + + function ExpensiveText({text, ms}) { + Scheduler.log(text); + Scheduler.unstable_advanceTime(ms); + return ; + } + + function App() { + return ( + <> + + }> + + + + + + + ); + } + + const root = ReactNoop.createRoot(); + root.render(); + await waitForAll(['1', 'Suspend! [Async]', 'Loading...']); + expect(root).toMatchRenderedOutput( + <> + + + , + ); + + await resolveText('Async'); + expect(root).toMatchRenderedOutput( + <> + + + , + ); + + await waitFor(['Async', 'A', 'B']); + ReactNoop.expire(100000); + await advanceTimers(100000); + setText('2'); + await waitForPaint(['2']); + + await waitForMicrotasks(); + Scheduler.unstable_flushNumberOfYields(1); + assertLog(['Async', 'A', 'B', 'C']); + + expect(root).toMatchRenderedOutput( + <> + + + + + + , + ); + }); });