diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index a6ee56a09061e..a207b5e9ef05c 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -2355,18 +2355,20 @@ function initSuspenseListRenderState( tail: null | Fiber, lastContentRow: null | Fiber, tailMode: SuspenseListTailMode, + lastEffectBeforeRendering: null | Fiber, ): void { let renderState: null | SuspenseListRenderState = workInProgress.memoizedState; if (renderState === null) { - workInProgress.memoizedState = { + workInProgress.memoizedState = ({ isBackwards: isBackwards, rendering: null, last: lastContentRow, tail: tail, tailExpiration: 0, tailMode: tailMode, - }; + lastEffect: lastEffectBeforeRendering, + }: SuspenseListRenderState); } else { // We can reuse the existing object from previous renders. renderState.isBackwards = isBackwards; @@ -2375,6 +2377,7 @@ function initSuspenseListRenderState( renderState.tail = tail; renderState.tailExpiration = 0; renderState.tailMode = tailMode; + renderState.lastEffect = lastEffectBeforeRendering; } } @@ -2456,6 +2459,7 @@ function updateSuspenseListComponent( tail, lastContentRow, tailMode, + workInProgress.lastEffect, ); break; } @@ -2487,6 +2491,7 @@ function updateSuspenseListComponent( tail, null, // last tailMode, + workInProgress.lastEffect, ); break; } @@ -2497,6 +2502,7 @@ function updateSuspenseListComponent( null, // tail null, // last undefined, + workInProgress.lastEffect, ); break; } diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 2436ec6af6a63..8bb417723fdfc 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -1053,7 +1053,10 @@ function completeWork( // Rerender the whole list, but this time, we'll force fallbacks // to stay in place. // Reset the effect list before doing the second pass since that's now invalid. - workInProgress.firstEffect = workInProgress.lastEffect = null; + if (renderState.lastEffect === null) { + workInProgress.firstEffect = null; + } + workInProgress.lastEffect = renderState.lastEffect; // Reset the child fibers to their original state. resetChildFibers(workInProgress, renderExpirationTime); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js index e22dc8ccd63cb..f41e7a5073eed 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js @@ -585,6 +585,90 @@ describe('ReactSuspenseList', () => { ); }); + it('displays all "together" during an update', async () => { + let A = createAsyncText('A'); + let B = createAsyncText('B'); + let C = createAsyncText('C'); + let D = createAsyncText('D'); + + function Foo({step}) { + return ( + + {step === 0 && ( + }> + + + )} + {step === 0 && ( + }> + + + )} + {step === 1 && ( + }> + + + )} + {step === 1 && ( + }> + + + )} + + ); + } + + // Mount + await A.resolve(); + ReactNoop.render(); + expect(Scheduler).toFlushAndYield([ + 'A', + 'Suspend! [B]', + 'Loading B', + 'Loading A', + 'Loading B', + ]); + expect(ReactNoop).toMatchRenderedOutput( + <> + Loading A + Loading B + , + ); + await B.resolve(); + expect(Scheduler).toFlushAndYield(['A', 'B']); + expect(ReactNoop).toMatchRenderedOutput( + <> + A + B + , + ); + + // Update + await C.resolve(); + ReactNoop.render(); + expect(Scheduler).toFlushAndYield([ + 'C', + 'Suspend! [D]', + 'Loading D', + 'Loading C', + 'Loading D', + ]); + expect(ReactNoop).toMatchRenderedOutput( + <> + Loading C + Loading D + , + ); + await D.resolve(); + expect(Scheduler).toFlushAndYield(['C', 'D']); + expect(ReactNoop).toMatchRenderedOutput( + <> + C + D + , + ); + }); + it('avoided boundaries can be coordinate with SuspenseList', async () => { let A = createAsyncText('A'); let B = createAsyncText('B');