Skip to content

Commit

Permalink
Selective Hydration: Don't suspend if showing fallback (#27230)
Browse files Browse the repository at this point in the history
A transition that flows into a dehydrated boundary should not suspend if
the boundary is showing a fallback.

This is related to another issue where Fizz streams in the initial HTML
after a client navigation has already happened. That issue is not fixed
by this commit, but it does make it less likely. Need to think more
about the larger issue.
  • Loading branch information
acdlite authored Aug 25, 2023
1 parent 9d4582d commit ab31a9e
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
51 changes: 51 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6176,4 +6176,55 @@ describe('ReactDOMFizzServer', () => {
// However, it does error the shell.
expect(fatalErrors).toEqual(['testing postpone']);
});

it(
'a transition that flows into a dehydrated boundary should not suspend ' +
'if the boundary is showing a fallback',
async () => {
let setSearch;
function App() {
const [search, _setSearch] = React.useState('initial query');
setSearch = _setSearch;
return (
<div>
<div>{search}</div>
<div>
<Suspense fallback="Loading...">
<AsyncText text="Async" />
</Suspense>
</div>
</div>
);
}

// Render the initial HTML, which is showing a fallback.
await act(() => {
const {pipe} = renderToPipeableStream(<App />);
pipe(writable);
});

// Start hydrating.
await clientAct(() => {
ReactDOMClient.hydrateRoot(container, <App />);
});
expect(getVisibleChildren(container)).toEqual(
<div>
<div>initial query</div>
<div>Loading...</div>
</div>,
);

// Before the HTML has streamed in, update the query. The part outside
// the fallback should be allowed to finish.
await clientAct(() => {
React.startTransition(() => setSearch('updated query'));
});
expect(getVisibleChildren(container)).toEqual(
<div>
<div>updated query</div>
<div>Loading...</div>
</div>,
);
},
);
});
10 changes: 9 additions & 1 deletion packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -2958,7 +2958,15 @@ function updateDehydratedSuspenseComponent(
// TODO: We should ideally have a sync hydration lane that we can apply to do
// a pass where we hydrate this subtree in place using the previous Context and then
// reapply the update afterwards.
renderDidSuspendDelayIfPossible();
if (isSuspenseInstancePending(suspenseInstance)) {
// This is a dehydrated suspense instance. We don't need to suspend
// because we're already showing a fallback.
// TODO: The Fizz runtime might still stream in completed HTML, out-of-
// band. Should we fix this? There's a version of this bug that happens
// during client rendering, too. Needs more consideration.
} else {
renderDidSuspendDelayIfPossible();
}
return retrySuspenseComponentWithoutHydrating(
current,
workInProgress,
Expand Down

1 comment on commit ab31a9e

@asshole86
Copy link

@asshole86 asshole86 commented on ab31a9e Nov 2, 2023 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.