Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit edf6eac

Browse files
authored
Don't cut off the tail of a SuspenseList if hydrating (facebook#18854)
1 parent 55f5cde commit edf6eac

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

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

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,99 @@ describe('ReactDOMServerPartialHydration', () => {
16981698
expect(container.textContent).toBe('ABC');
16991699
});
17001700

1701+
// @gate experimental
1702+
it('clears server boundaries when SuspenseList does a second pass', async () => {
1703+
let suspend = false;
1704+
let resolve;
1705+
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
1706+
1707+
const ref = React.createRef();
1708+
1709+
function Child({children}) {
1710+
if (suspend) {
1711+
throw promise;
1712+
} else {
1713+
return children;
1714+
}
1715+
}
1716+
1717+
function Before() {
1718+
Scheduler.unstable_yieldValue('Before');
1719+
return null;
1720+
}
1721+
1722+
function After() {
1723+
Scheduler.unstable_yieldValue('After');
1724+
return null;
1725+
}
1726+
1727+
function FirstRow() {
1728+
return (
1729+
<>
1730+
<Before />
1731+
<Suspense fallback="Loading A">
1732+
<span>A</span>
1733+
</Suspense>
1734+
<After />
1735+
</>
1736+
);
1737+
}
1738+
1739+
function App() {
1740+
return (
1741+
<Suspense fallback={null}>
1742+
<SuspenseList revealOrder="forwards" tail="hidden">
1743+
<FirstRow />
1744+
<Suspense fallback="Loading B">
1745+
<Child>
1746+
<span ref={ref}>B</span>
1747+
</Child>
1748+
</Suspense>
1749+
</SuspenseList>
1750+
</Suspense>
1751+
);
1752+
}
1753+
1754+
suspend = false;
1755+
const html = ReactDOMServer.renderToString(<App />);
1756+
expect(Scheduler).toHaveYielded(['Before', 'After']);
1757+
1758+
const container = document.createElement('div');
1759+
container.innerHTML = html;
1760+
1761+
const b = container.getElementsByTagName('span')[1];
1762+
expect(b.textContent).toBe('B');
1763+
1764+
const root = ReactDOM.createRoot(container, {hydrate: true});
1765+
1766+
// Increase hydration priority to higher than "offscreen".
1767+
ReactDOM.unstable_scheduleHydration(b);
1768+
1769+
suspend = true;
1770+
1771+
await act(async () => {
1772+
root.render(<App />);
1773+
expect(Scheduler).toFlushAndYieldThrough(['Before']);
1774+
// This took a long time to render.
1775+
Scheduler.unstable_advanceTime(1000);
1776+
expect(Scheduler).toFlushAndYield(['After']);
1777+
// This will cause us to skip the second row completely.
1778+
});
1779+
1780+
// We haven't hydrated the second child but the placeholder is still in the list.
1781+
expect(ref.current).toBe(null);
1782+
expect(container.textContent).toBe('AB');
1783+
1784+
suspend = false;
1785+
await act(async () => {
1786+
// Resolve the boundary to be in its resolved final state.
1787+
await resolve();
1788+
});
1789+
1790+
expect(container.textContent).toBe('AB');
1791+
expect(ref.current).toBe(b);
1792+
});
1793+
17011794
// @gate experimental
17021795
it('can client render nested boundaries', async () => {
17031796
let suspend = false;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ import {
118118
prepareToHydrateHostSuspenseInstance,
119119
popHydrationState,
120120
resetHydrationState,
121+
getIsHydrating,
121122
} from './ReactFiberHydrationContext.new';
122123
import {
123124
enableSchedulerTracing,
@@ -579,6 +580,11 @@ function cutOffTailIfNeeded(
579580
renderState: SuspenseListRenderState,
580581
hasRenderedATailFallback: boolean,
581582
) {
583+
if (getIsHydrating()) {
584+
// If we're hydrating, we should consume as many items as we can
585+
// so we don't leave any behind.
586+
return;
587+
}
582588
switch (renderState.tailMode) {
583589
case 'hidden': {
584590
// Any insertions at the end of the tail list after this point

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ import {
115115
prepareToHydrateHostSuspenseInstance,
116116
popHydrationState,
117117
resetHydrationState,
118+
getIsHydrating,
118119
} from './ReactFiberHydrationContext.old';
119120
import {
120121
enableSchedulerTracing,
@@ -575,6 +576,11 @@ function cutOffTailIfNeeded(
575576
renderState: SuspenseListRenderState,
576577
hasRenderedATailFallback: boolean,
577578
) {
579+
if (getIsHydrating()) {
580+
// If we're hydrating, we should consume as many items as we can
581+
// so we don't leave any behind.
582+
return;
583+
}
578584
switch (renderState.tailMode) {
579585
case 'hidden': {
580586
// Any insertions at the end of the tail list after this point

0 commit comments

Comments
 (0)