Skip to content

Commit 2f79bce

Browse files
authored
Fix webpack/terser startTransition minification bug in production mode (#10588)
1 parent ae0067b commit 2f79bce

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"react-router": patch
3+
"react-router-dom": patch
4+
---
5+
6+
Work around webpack/terser `React.startTransition` minification bug in production mode

packages/react-router-dom/index.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,24 @@ export interface BrowserRouterProps {
300300
window?: Window;
301301
}
302302

303-
// Webpack + React 17 fails to compile on the usage of `React.startTransition` or
304-
// `React["startTransition"]` even if it's behind a feature detection of
305-
// `"startTransition" in React`. Moving this to a constant avoids the issue :/
303+
// Webpack + React 17 fails to compile on any of the following:
304+
// * import { startTransition } from "react"
305+
// * import * as React from from "react";
306+
// "startTransition" in React ? React.startTransition(() => setState()) : setState()
307+
// * import * as React from from "react";
308+
// "startTransition" in React ? React["startTransition"](() => setState()) : setState()
309+
//
310+
// Moving it to a constant such as the following solves the Webpack/React 17 issue:
311+
// * import * as React from from "react";
312+
// const START_TRANSITION = "startTransition";
313+
// START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState()
314+
//
315+
// However, that introduces webpack/terser minification issues in production builds
316+
// in React 18 where minification/obfuscation ends up removing the call of
317+
// React.startTransition entirely from the first half of the ternary. Grabbing
318+
// this reference once up front resolves that issue.
306319
const START_TRANSITION = "startTransition";
320+
const startTransitionImpl = React[START_TRANSITION];
307321

308322
/**
309323
* A `<Router>` for use in web browsers. Provides the cleanest URLs.
@@ -325,8 +339,8 @@ export function BrowserRouter({
325339
});
326340
let setState = React.useCallback(
327341
(newState: { action: NavigationType; location: Location }) => {
328-
START_TRANSITION in React
329-
? React[START_TRANSITION](() => setStateImpl(newState))
342+
startTransitionImpl
343+
? startTransitionImpl(() => setStateImpl(newState))
330344
: setStateImpl(newState);
331345
},
332346
[setStateImpl]
@@ -368,8 +382,8 @@ export function HashRouter({ basename, children, window }: HashRouterProps) {
368382
});
369383
let setState = React.useCallback(
370384
(newState: { action: NavigationType; location: Location }) => {
371-
START_TRANSITION in React
372-
? React[START_TRANSITION](() => setStateImpl(newState))
385+
startTransitionImpl
386+
? startTransitionImpl(() => setStateImpl(newState))
373387
: setStateImpl(newState);
374388
},
375389
[setStateImpl]
@@ -407,8 +421,8 @@ function HistoryRouter({ basename, children, history }: HistoryRouterProps) {
407421
});
408422
let setState = React.useCallback(
409423
(newState: { action: NavigationType; location: Location }) => {
410-
START_TRANSITION in React
411-
? React[START_TRANSITION](() => setStateImpl(newState))
424+
startTransitionImpl
425+
? startTransitionImpl(() => setStateImpl(newState))
412426
: setStateImpl(newState);
413427
},
414428
[setStateImpl]

packages/react-router/lib/components.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,24 @@ export interface RouterProviderProps {
5454
router: RemixRouter;
5555
}
5656

57-
// Webpack + React 17 fails to compile on the usage of `React.startTransition` or
58-
// `React["startTransition"]` even if it's behind a feature detection of
59-
// `"startTransition" in React`. Moving this to a constant avoids the issue :/
57+
// Webpack + React 17 fails to compile on any of the following:
58+
// * import { startTransition } from "react"
59+
// * import * as React from from "react";
60+
// "startTransition" in React ? React.startTransition(() => setState()) : setState()
61+
// * import * as React from from "react";
62+
// "startTransition" in React ? React["startTransition"](() => setState()) : setState()
63+
//
64+
// Moving it to a constant such as the following solves the Webpack/React 17 issue:
65+
// * import * as React from from "react";
66+
// const START_TRANSITION = "startTransition";
67+
// START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState()
68+
//
69+
// However, that introduces webpack/terser minification issues in production builds
70+
// in React 18 where minification/obfuscation ends up removing the call of
71+
// React.startTransition entirely from the first half of the ternary. Grabbing
72+
// this reference once up front resolves that issue.
6073
const START_TRANSITION = "startTransition";
74+
const startTransitionImpl = React[START_TRANSITION];
6175

6276
/**
6377
* Given a Remix Router instance, render the appropriate UI
@@ -71,8 +85,8 @@ export function RouterProvider({
7185
let [state, setStateImpl] = React.useState(router.state);
7286
let setState = React.useCallback(
7387
(newState: RouterState) => {
74-
START_TRANSITION in React
75-
? React[START_TRANSITION](() => setStateImpl(newState))
88+
startTransitionImpl
89+
? startTransitionImpl(() => setStateImpl(newState))
7690
: setStateImpl(newState);
7791
},
7892
[setStateImpl]
@@ -183,8 +197,8 @@ export function MemoryRouter({
183197
});
184198
let setState = React.useCallback(
185199
(newState: { action: NavigationType; location: Location }) => {
186-
START_TRANSITION in React
187-
? React[START_TRANSITION](() => setStateImpl(newState))
200+
startTransitionImpl
201+
? startTransitionImpl(() => setStateImpl(newState))
188202
: setStateImpl(newState);
189203
},
190204
[setStateImpl]

0 commit comments

Comments
 (0)