Skip to content

Commit e5b9efb

Browse files
committed
[Float] Suspend unstyled content for up to 1 minute
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.
1 parent 179bb4a commit e5b9efb

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

packages/react-dom-bindings/src/client/ReactDOMHostConfig.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3343,6 +3343,13 @@ export function waitForCommitToBeReady(): null | (Function => Function) {
33433343
}
33443344

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

33583365
function onUnsuspend(this: SuspendedState) {

packages/react-dom/src/__tests__/ReactDOMFloat-test.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,7 +3163,7 @@ body {
31633163
);
31643164
});
31653165

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

3194-
jest.advanceTimersByTime(1000);
3194+
// Advance time by 50 seconds. Even still, the transition is suspended.
3195+
jest.advanceTimersByTime(50000);
3196+
await waitForAll([]);
3197+
expect(getMeaningfulChildren(document)).toEqual(
3198+
<html>
3199+
<head>
3200+
<link rel="preload" href="foo" as="style" />
3201+
</head>
3202+
<body />
3203+
</html>,
3204+
);
3205+
3206+
// Advance time by 10 seconds more. A full minute total has elapsed. At this
3207+
// point, something must have really gone wrong, so we time out and allow
3208+
// unstyled content to be displayed.
3209+
jest.advanceTimersByTime(10000);
31953210
expect(getMeaningfulChildren(document)).toEqual(
31963211
<html>
31973212
<head>

0 commit comments

Comments
 (0)