diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js
index 1d5ac8fcc4bcf..ba99ac80abce3 100644
--- a/packages/react-reconciler/src/ReactFiberBeginWork.js
+++ b/packages/react-reconciler/src/ReactFiberBeginWork.js
@@ -3153,6 +3153,7 @@ function beginWork(
// update in the past but didn't complete it.
renderState.rendering = null;
renderState.tail = null;
+ renderState.lastEffect = null;
}
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js
index 7928ca491b082..c13c311448285 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.internal.js
@@ -2300,4 +2300,189 @@ describe('ReactSuspenseList', () => {
// remount.
expect(previousInst).toBe(setAsyncB);
});
+
+ it('is able to re-suspend the last rows during an update with hidden', async () => {
+ let AsyncB = createAsyncText('B');
+
+ let setAsyncB;
+
+ function B() {
+ let [shouldBeAsync, setAsync] = React.useState(false);
+ setAsyncB = setAsync;
+
+ return shouldBeAsync ? (
+ }>
+
+
+ ) : (
+
+ );
+ }
+
+ function Foo({updateList}) {
+ return (
+
+ }>
+
+
+
+
+ );
+ }
+
+ ReactNoop.render();
+
+ expect(Scheduler).toFlushAndYield(['A', 'Sync B']);
+
+ expect(ReactNoop).toMatchRenderedOutput(
+ <>
+ A
+ Sync B
+ >,
+ );
+
+ let previousInst = setAsyncB;
+
+ // During an update we suspend on B.
+ ReactNoop.act(() => setAsyncB(true));
+
+ expect(Scheduler).toHaveYielded([
+ 'Suspend! [B]',
+ 'Loading B',
+ // The second pass is the "force hide" pass
+ 'Loading B',
+ ]);
+
+ expect(ReactNoop).toMatchRenderedOutput(
+ <>
+ A
+ Loading B
+ >,
+ );
+
+ // Before we resolve we'll rerender the whole list.
+ // This should leave the tree intact.
+ ReactNoop.act(() => ReactNoop.render());
+
+ expect(Scheduler).toHaveYielded(['A', 'Suspend! [B]', 'Loading B']);
+
+ expect(ReactNoop).toMatchRenderedOutput(
+ <>
+ A
+ Loading B
+ >,
+ );
+
+ await AsyncB.resolve();
+
+ expect(Scheduler).toFlushAndYield(['B']);
+
+ expect(ReactNoop).toMatchRenderedOutput(
+ <>
+ A
+ B
+ >,
+ );
+
+ // This should be the same instance. I.e. it didn't
+ // remount.
+ expect(previousInst).toBe(setAsyncB);
+ });
+
+ it('is able to interrupt a partially rendered tree and continue later', async () => {
+ let AsyncA = createAsyncText('A');
+
+ let updateLowPri;
+ let updateHighPri;
+
+ function Bar() {
+ let [highPriState, setHighPriState] = React.useState(false);
+ updateHighPri = setHighPriState;
+ return highPriState ? : null;
+ }
+
+ function Foo() {
+ let [lowPriState, setLowPriState] = React.useState(false);
+ updateLowPri = setLowPriState;
+ return (
+
+ }>
+
+
+ {lowPriState ? : null}
+ {lowPriState ? : null}
+ {lowPriState ? : null}
+
+ );
+ }
+
+ ReactNoop.render();
+
+ expect(Scheduler).toFlushAndYield([]);
+
+ expect(ReactNoop).toMatchRenderedOutput(null);
+
+ ReactNoop.act(() => {
+ // Add a few items at the end.
+ updateLowPri(true);
+
+ // Flush partially through.
+ expect(Scheduler).toFlushAndYieldThrough(['B', 'C']);
+
+ // Schedule another update at higher priority.
+ Scheduler.unstable_runWithPriority(
+ Scheduler.unstable_UserBlockingPriority,
+ () => updateHighPri(true),
+ );
+
+ // That will intercept the previous render.
+ });
+
+ jest.runAllTimers();
+
+ expect(Scheduler).toHaveYielded(
+ __DEV__
+ ? [
+ // First attempt at high pri.
+ 'Suspend! [A]',
+ 'Loading A',
+ // Re-render at forced.
+ 'Suspend! [A]',
+ 'Loading A',
+ // We auto-commit this on DEV.
+ // Try again on low-pri.
+ 'Suspend! [A]',
+ 'Loading A',
+ ]
+ : [
+ // First attempt at high pri.
+ 'Suspend! [A]',
+ 'Loading A',
+ // Re-render at forced.
+ 'Suspend! [A]',
+ 'Loading A',
+ // We didn't commit so retry at low-pri.
+ 'Suspend! [A]',
+ 'Loading A',
+ // Re-render at forced.
+ 'Suspend! [A]',
+ 'Loading A',
+ ],
+ );
+
+ expect(ReactNoop).toMatchRenderedOutput(Loading A);
+
+ await AsyncA.resolve();
+
+ expect(Scheduler).toFlushAndYield(['A', 'B', 'C', 'D']);
+
+ expect(ReactNoop).toMatchRenderedOutput(
+ <>
+ A
+ B
+ C
+ D
+ >,
+ );
+ });
});