@samitouri / QOS-React-1 / commits / 5717f1933f

[react-dom] Enforce small gap between completed navigation and default Transition indicator (#33354)

Sebastian "Sebbie" Silbermann committed May 28, 2025 at 19:46 UTC 5717f1933f2e8b10406fde1043c3047cbfbddc82
3 files changed +45 -2
fixtures/flight/src/App.js
+2
@@ -12,6 +12,7 @@ import Button from './Button.js';
12 import Form from './Form.js';
13 import {Dynamic} from './Dynamic.js';
14 import {Client} from './Client.js';
15 +import {Navigate} from './Navigate.js';
16
17 import {Note} from './cjs/Note.js';
18
@@ -89,6 +90,7 @@ export default async function App({prerender}) {
90 <Note />
91 <Foo>{dedupedChild}</Foo>
92 <Bar>{Promise.resolve([dedupedChild])}</Bar>
93 + <Navigate />
94 </Container>
95 </body>
96 </html>
fixtures/flight/src/Navigate.js new
+40
@@ -0,0 +1,40 @@
1 +'use client';
2 +
3 +import * as React from 'react';
4 +import Container from './Container.js';
5 +
6 +export function Navigate() {
7 + /** Repro for https://issues.chromium.org/u/1/issues/419746417 */
8 + function provokeChromeCrash() {
9 + React.startTransition(async () => {
10 + console.log('Default transition triggered');
11 +
12 + await new Promise(resolve => {
13 + setTimeout(
14 + () => {
15 + history.pushState(
16 + {},
17 + '',
18 + `?chrome-crash-419746417=${performance.now()}`
19 + );
20 + },
21 + // This needs to happen before React's default transition indicator
22 + // is displayed but after it's scheduled.
23 + 100 + -50
24 + );
25 +
26 + setTimeout(() => {
27 + console.log('Default transition completed');
28 + resolve();
29 + }, 1000);
30 + });
31 + });
32 + }
33 +
34 + return (
35 + <Container>
36 + <h2>Navigation fixture</h2>
37 + <button onClick={provokeChromeCrash}>Provoke Chrome Crash (fixed)</button>
38 + </Container>
39 + );
40 +}
packages/react-dom/src/client/ReactDOMDefaultTransitionIndicator.js
+3 -2
@@ -38,7 +38,8 @@ export function defaultOnDefaultTransitionIndicator(): void | (() => void) {
38 if (!isCancelled) {
39 // Some other navigation completed but we should still be running.
40 // Start another fake one to keep the loading indicator going.
41 - startFakeNavigation();
41 + // There needs to be an async gap to work around https://issues.chromium.org/u/1/issues/419746417.
42 + setTimeout(startFakeNavigation, 20);
43 }
44 }
45
@@ -70,7 +71,7 @@ export function defaultOnDefaultTransitionIndicator(): void | (() => void) {
71 }
72 }
73
73 - // Delay the start a bit in case this is a fast navigation.
74 + // Delay the start a bit in case this is a fast Transition.
75 setTimeout(startFakeNavigation, 100);
76
77 return function () {