diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
index dd1c7548c403..bd41a9361e8a 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
@@ -1699,7 +1699,7 @@ describe('ReactDOMServerPartialHydration', () => {
});
// @gate experimental
- it('clears server boundaries when SuspenseList does a second pass', async () => {
+ it('clears server boundaries when SuspenseList runs out of time hydrating', async () => {
let suspend = false;
let resolve;
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
@@ -1791,6 +1791,63 @@ describe('ReactDOMServerPartialHydration', () => {
expect(ref.current).toBe(b);
});
+ // @gate experimental
+ it('clears server boundaries when SuspenseList suspends last row hydrating', async () => {
+ let suspend = false;
+ let resolve;
+ const promise = new Promise(resolvePromise => (resolve = resolvePromise));
+
+ function Child({children}) {
+ if (suspend) {
+ throw promise;
+ } else {
+ return children;
+ }
+ }
+
+ function App() {
+ return (
+
+
+
+ A
+
+
+
+ B
+
+
+
+
+ );
+ }
+
+ suspend = true;
+ const html = ReactDOMServer.renderToString();
+
+ const container = document.createElement('div');
+ container.innerHTML = html;
+
+ const root = ReactDOM.createRoot(container, {hydrate: true});
+
+ suspend = true;
+
+ await act(async () => {
+ root.render();
+ });
+
+ // We haven't hydrated the second child but the placeholder is still in the list.
+ expect(container.textContent).toBe('ALoading B');
+
+ suspend = false;
+ await act(async () => {
+ // Resolve the boundary to be in its resolved final state.
+ await resolve();
+ });
+
+ expect(container.textContent).toBe('AB');
+ });
+
// @gate experimental
it('can client render nested boundaries', async () => {
let suspend = false;
diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js
index 577224097c24..8e78d36e6a04 100644
--- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js
@@ -1098,7 +1098,8 @@ function completeWork(
if (
renderState.tail === null &&
renderState.tailMode === 'hidden' &&
- !renderedTail.alternate
+ !renderedTail.alternate &&
+ !getIsHydrating() // We don't cut it if we're hydrating.
) {
// We need to delete the row we just rendered.
// Reset the effect list to what it was before we rendered this
diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
index cc7ed6017ed7..ebdc40ed0e4d 100644
--- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
@@ -1114,7 +1114,8 @@ function completeWork(
if (
renderState.tail === null &&
renderState.tailMode === 'hidden' &&
- !renderedTail.alternate
+ !renderedTail.alternate &&
+ !getIsHydrating() // We don't cut it if we're hydrating.
) {
// We need to delete the row we just rendered.
// Reset the effect list to what it was before we rendered this