Skip to content

Commit

Permalink
[Float] Suspend unstyled content for up to 1 minute
Browse files Browse the repository at this point in the history
We almost never want to show content before its styles have loaded. But
eventually we will give up and allow unstyled content. So this extends
the timeout to a full minute. This somewhat arbitrary — big enough that
you'd only reach it under extreme circumstances.

Note that, like regular Suspense, the app is still interactive while
we're waiting for content to load. Only the unstyled content is blocked
from appearing, not updates in general. A new update will interupt it.

We should out what the browser engines do during initial page load and
consider aligning our behavior with that. It's supposed to be render
blocking by default but there may be some cases where they, too, give up
and FOUC.
  • Loading branch information
acdlite committed Mar 31, 2023
1 parent 179bb4a commit e5b9efb
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 3 deletions.
9 changes: 8 additions & 1 deletion packages/react-dom-bindings/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -3343,6 +3343,13 @@ export function waitForCommitToBeReady(): null | (Function => Function) {
}

function unsuspendAfterTimeout(state: SuspendedState) {
// We almost never want to show content before its styles have loaded. But
// eventually we will give up and allow unstyled content. So this number is
// somewhat arbitrary — big enough that you'd only reach it under
// extreme circumstances.
// TODO: Figure out what the browser engines do during initial page load and
// consider aligning our behavior with that.
const stylesheetTimeout = 60000; // one minute
setTimeout(() => {
if (state.stylesheets) {
insertSuspendedStylesheets(state, state.stylesheets);
Expand All @@ -3352,7 +3359,7 @@ function unsuspendAfterTimeout(state: SuspendedState) {
state.unsuspend = null;
unsuspend();
}
}, 500);
}, stylesheetTimeout);
}

function onUnsuspend(this: SuspendedState) {
Expand Down
19 changes: 17 additions & 2 deletions packages/react-dom/src/__tests__/ReactDOMFloat-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3163,7 +3163,7 @@ body {
);
});

it('can unsuspend after a timeout even if some assets never load', async () => {
it('stylesheets block render, with a really long timeout', async () => {
function App({children}) {
return (
<html>
Expand Down Expand Up @@ -3191,7 +3191,22 @@ body {
</html>,
);

jest.advanceTimersByTime(1000);
// Advance time by 50 seconds. Even still, the transition is suspended.
jest.advanceTimersByTime(50000);
await waitForAll([]);
expect(getMeaningfulChildren(document)).toEqual(
<html>
<head>
<link rel="preload" href="foo" as="style" />
</head>
<body />
</html>,
);

// Advance time by 10 seconds more. A full minute total has elapsed. At this
// point, something must have really gone wrong, so we time out and allow
// unstyled content to be displayed.
jest.advanceTimersByTime(10000);
expect(getMeaningfulChildren(document)).toEqual(
<html>
<head>
Expand Down

0 comments on commit e5b9efb

Please sign in to comment.