diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 338191f23666..b88f17c1cb16 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -1681,11 +1681,7 @@ function getRemainingWorkInPrimaryTree( // This boundary already timed out. Check if this render includes the level // that previously suspended. const baseTime = currentSuspenseState.baseTime; - if ( - baseTime !== NoWork && - baseTime < renderExpirationTime && - baseTime > currentChildExpirationTime - ) { + if (baseTime !== NoWork && baseTime < renderExpirationTime) { // There's pending work at a lower level that might now be unblocked. return baseTime; } diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 4fde99d80c71..6fbfd5cd9707 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -1681,11 +1681,7 @@ function getRemainingWorkInPrimaryTree( // This boundary already timed out. Check if this render includes the level // that previously suspended. const baseTime = currentSuspenseState.baseTime; - if ( - baseTime !== NoWork && - baseTime < renderExpirationTime && - baseTime > currentChildExpirationTime - ) { + if (baseTime !== NoWork && baseTime < renderExpirationTime) { // There's pending work at a lower level that might now be unblocked. return baseTime; } diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js index cfe3d244b510..5f5469bcd904 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js @@ -3869,4 +3869,53 @@ describe('ReactSuspenseWithNoopRenderer', () => { expect(root).toMatchRenderedOutput(); }); }); + + it('regression: #18657', async () => { + const {useState} = React; + + let setText; + function App() { + const [text, _setText] = useState('A'); + setText = _setText; + return ; + } + + const root = ReactNoop.createRoot(); + await ReactNoop.act(async () => { + await resolveText('A'); + root.render( + }> + + , + ); + }); + expect(Scheduler).toHaveYielded(['A']); + expect(root).toMatchRenderedOutput(); + + await ReactNoop.act(async () => { + setText('B'); + Scheduler.unstable_runWithPriority( + Scheduler.unstable_IdlePriority, + () => { + setText('B'); + }, + ); + // Suspend the first update. The second update doesn't run because it has + // Idle priority. + expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']); + + // Commit the fallback. Now we'll try working on Idle. + jest.runAllTimers(); + + // It also suspends. + expect(Scheduler).toFlushAndYield(['Suspend! [B]']); + }); + + await ReactNoop.act(async () => { + setText('B'); + await resolveText('B'); + }); + expect(Scheduler).toHaveYielded(['Promise resolved [B]', 'B']); + expect(root).toMatchRenderedOutput(); + }); });