Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't cut off effects at end of list if hydrating #18872

Merged
merged 1 commit into from May 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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));
Expand Down Expand Up @@ -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 (
<Suspense fallback={null}>
<SuspenseList revealOrder="forwards" tail="hidden">
<Suspense fallback="Loading A">
<span>A</span>
</Suspense>
<Suspense fallback="Loading B">
<Child>
<span>B</span>
</Child>
</Suspense>
</SuspenseList>
</Suspense>
);
}

suspend = true;
const html = ReactDOMServer.renderToString(<App />);

const container = document.createElement('div');
container.innerHTML = html;

const root = ReactDOM.createRoot(container, {hydrate: true});

suspend = true;

await act(async () => {
root.render(<App />);
});

// 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;
Expand Down
3 changes: 2 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.new.js
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.old.js
Expand Up @@ -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
Expand Down