Skip to content

Commit 539527b

Browse files
authored
Don't cut off effects at end of list if hydrating (facebook#18872)
1 parent fd696df commit 539527b

File tree

3 files changed

+62
-3
lines changed

3 files changed

+62
-3
lines changed

packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,7 +1699,7 @@ describe('ReactDOMServerPartialHydration', () => {
16991699
});
17001700

17011701
// @gate experimental
1702-
it('clears server boundaries when SuspenseList does a second pass', async () => {
1702+
it('clears server boundaries when SuspenseList runs out of time hydrating', async () => {
17031703
let suspend = false;
17041704
let resolve;
17051705
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
@@ -1791,6 +1791,63 @@ describe('ReactDOMServerPartialHydration', () => {
17911791
expect(ref.current).toBe(b);
17921792
});
17931793

1794+
// @gate experimental
1795+
it('clears server boundaries when SuspenseList suspends last row hydrating', async () => {
1796+
let suspend = false;
1797+
let resolve;
1798+
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
1799+
1800+
function Child({children}) {
1801+
if (suspend) {
1802+
throw promise;
1803+
} else {
1804+
return children;
1805+
}
1806+
}
1807+
1808+
function App() {
1809+
return (
1810+
<Suspense fallback={null}>
1811+
<SuspenseList revealOrder="forwards" tail="hidden">
1812+
<Suspense fallback="Loading A">
1813+
<span>A</span>
1814+
</Suspense>
1815+
<Suspense fallback="Loading B">
1816+
<Child>
1817+
<span>B</span>
1818+
</Child>
1819+
</Suspense>
1820+
</SuspenseList>
1821+
</Suspense>
1822+
);
1823+
}
1824+
1825+
suspend = true;
1826+
const html = ReactDOMServer.renderToString(<App />);
1827+
1828+
const container = document.createElement('div');
1829+
container.innerHTML = html;
1830+
1831+
const root = ReactDOM.createRoot(container, {hydrate: true});
1832+
1833+
suspend = true;
1834+
1835+
await act(async () => {
1836+
root.render(<App />);
1837+
});
1838+
1839+
// We haven't hydrated the second child but the placeholder is still in the list.
1840+
expect(container.textContent).toBe('ALoading B');
1841+
1842+
suspend = false;
1843+
await act(async () => {
1844+
// Resolve the boundary to be in its resolved final state.
1845+
await resolve();
1846+
});
1847+
1848+
expect(container.textContent).toBe('AB');
1849+
});
1850+
17941851
// @gate experimental
17951852
it('can client render nested boundaries', async () => {
17961853
let suspend = false;

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,8 @@ function completeWork(
10981098
if (
10991099
renderState.tail === null &&
11001100
renderState.tailMode === 'hidden' &&
1101-
!renderedTail.alternate
1101+
!renderedTail.alternate &&
1102+
!getIsHydrating() // We don't cut it if we're hydrating.
11021103
) {
11031104
// We need to delete the row we just rendered.
11041105
// Reset the effect list to what it was before we rendered this

packages/react-reconciler/src/ReactFiberCompleteWork.old.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,8 @@ function completeWork(
11141114
if (
11151115
renderState.tail === null &&
11161116
renderState.tailMode === 'hidden' &&
1117-
!renderedTail.alternate
1117+
!renderedTail.alternate &&
1118+
!getIsHydrating() // We don't cut it if we're hydrating.
11181119
) {
11191120
// We need to delete the row we just rendered.
11201121
// Reset the effect list to what it was before we rendered this

0 commit comments

Comments
 (0)