From 539527b642101fbfc0b4ba614d7e253905f46b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Fri, 8 May 2020 21:26:51 -0700 Subject: [PATCH] Don't cut off effects at end of list if hydrating (#18872) --- ...DOMServerPartialHydration-test.internal.js | 59 ++++++++++++++++++- .../src/ReactFiberCompleteWork.new.js | 3 +- .../src/ReactFiberCompleteWork.old.js | 3 +- 3 files changed, 62 insertions(+), 3 deletions(-) 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