Skip to content

Commit

Permalink
Fix webpack/terser startTransition minification bug in production mode (
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Jun 12, 2023
1 parent ae0067b commit 2f79bce
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 16 deletions.
6 changes: 6 additions & 0 deletions .changeset/start-transition-minification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"react-router": patch
"react-router-dom": patch
---

Work around webpack/terser `React.startTransition` minification bug in production mode
32 changes: 23 additions & 9 deletions packages/react-router-dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,24 @@ export interface BrowserRouterProps {
window?: Window;
}

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

/**
* A `<Router>` for use in web browsers. Provides the cleanest URLs.
Expand All @@ -325,8 +339,8 @@ export function BrowserRouter({
});
let setState = React.useCallback(
(newState: { action: NavigationType; location: Location }) => {
START_TRANSITION in React
? React[START_TRANSITION](() => setStateImpl(newState))
startTransitionImpl
? startTransitionImpl(() => setStateImpl(newState))
: setStateImpl(newState);
},
[setStateImpl]
Expand Down Expand Up @@ -368,8 +382,8 @@ export function HashRouter({ basename, children, window }: HashRouterProps) {
});
let setState = React.useCallback(
(newState: { action: NavigationType; location: Location }) => {
START_TRANSITION in React
? React[START_TRANSITION](() => setStateImpl(newState))
startTransitionImpl
? startTransitionImpl(() => setStateImpl(newState))
: setStateImpl(newState);
},
[setStateImpl]
Expand Down Expand Up @@ -407,8 +421,8 @@ function HistoryRouter({ basename, children, history }: HistoryRouterProps) {
});
let setState = React.useCallback(
(newState: { action: NavigationType; location: Location }) => {
START_TRANSITION in React
? React[START_TRANSITION](() => setStateImpl(newState))
startTransitionImpl
? startTransitionImpl(() => setStateImpl(newState))
: setStateImpl(newState);
},
[setStateImpl]
Expand Down
28 changes: 21 additions & 7 deletions packages/react-router/lib/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,24 @@ export interface RouterProviderProps {
router: RemixRouter;
}

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

/**
* Given a Remix Router instance, render the appropriate UI
Expand All @@ -71,8 +85,8 @@ export function RouterProvider({
let [state, setStateImpl] = React.useState(router.state);
let setState = React.useCallback(
(newState: RouterState) => {
START_TRANSITION in React
? React[START_TRANSITION](() => setStateImpl(newState))
startTransitionImpl
? startTransitionImpl(() => setStateImpl(newState))
: setStateImpl(newState);
},
[setStateImpl]
Expand Down Expand Up @@ -183,8 +197,8 @@ export function MemoryRouter({
});
let setState = React.useCallback(
(newState: { action: NavigationType; location: Location }) => {
START_TRANSITION in React
? React[START_TRANSITION](() => setStateImpl(newState))
startTransitionImpl
? startTransitionImpl(() => setStateImpl(newState))
: setStateImpl(newState);
},
[setStateImpl]
Expand Down

0 comments on commit 2f79bce

Please sign in to comment.