Skip to content

Commit b2c902b

Browse files
authored
Migrate react-router from declarative mode to data mode (#13534)
<!-- CURSOR_SUMMARY --> > [!NOTE] > Migrates routing from declarative `<Routes>` in `App` to React Router’s data router for better code-splitting and structure. > > - Introduces `createRoutes` in `app/routes.tsx` with a `RootLayout` wrapping providers and `CoinflowPurchaseProtection`; sets navigation ref via `NavigationSetup` > - Replaces `BrowserRouter/HashRouter` with `createBrowserRouter`/`createHashRouter` and `RouterProvider` in `AppProviders`; `App` now just returns `<AppProviders />` > - Ports `web-player/WebPlayer` to TypeScript and refactors route definitions to be lazy-loaded; maintains mobile/desktop route differences, redirects, and update banners > - Converts `pages/modals/AppModal` to default export and updates imports; `AnimatedSwitch` returns a React fragment wrapper; `HeaderContextConsumer` now returns `null` unless header is a valid element > - Cleans `tsconfig.json` excludes (remove storybook-specific excludes) > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 819533a. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 4caf103 commit b2c902b

File tree

10 files changed

+1841
-1286
lines changed

10 files changed

+1841
-1286
lines changed

packages/web/src/app/App.tsx

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,7 @@
11
// @refresh reset
2-
import { Suspense, lazy } from 'react'
3-
4-
import { route } from '@audius/common/utils'
5-
import { CoinflowPurchaseProtection } from '@coinflowlabs/react'
6-
import { Navigate, Route, Routes } from 'react-router'
7-
8-
import { AppModal } from 'pages/modals/AppModal'
9-
import { SomethingWrong } from 'pages/something-wrong/SomethingWrong'
10-
import { env } from 'services/env'
11-
12-
import { AppErrorBoundary } from './AppErrorBoundary'
2+
// App component - routing is now handled by data router in AppProviders
133
import { AppProviders } from './AppProviders'
14-
import WebPlayer from './web-player/WebPlayer'
15-
16-
const {
17-
PRIVATE_KEY_EXPORTER_SETTINGS_PAGE,
18-
SIGN_IN_PAGE,
19-
SIGN_ON_ALIASES,
20-
SIGN_UP_PAGE
21-
} = route
22-
23-
const SignOnPage = lazy(() => import('pages/sign-on-page'))
24-
const OAuthLoginPage = lazy(() => import('pages/oauth-login-page'))
25-
const OAuthPayPage = lazy(() => import('pages/oauth-pay-page'))
26-
const PrivateKeyExporterPage = lazy(
27-
() => import('pages/private-key-exporter-page/PrivateKeyExporterPage')
28-
)
29-
const PrivateKeyExporterModal = lazy(
30-
() => import('pages/private-key-exporter-page/PrivateKeyExporterModal')
31-
)
32-
33-
const MERCHANT_ID = env.COINFLOW_MERCHANT_ID
34-
const IS_PRODUCTION = env.ENVIRONMENT === 'production'
354

365
export const App = () => {
37-
return (
38-
<AppProviders>
39-
<SomethingWrong />
40-
<Suspense fallback={null}>
41-
<CoinflowPurchaseProtection
42-
merchantId={MERCHANT_ID || ''}
43-
coinflowEnv={IS_PRODUCTION ? 'prod' : 'sandbox'}
44-
/>
45-
<Routes>
46-
{SIGN_ON_ALIASES.map((a) => (
47-
<Route
48-
key={a}
49-
path={a}
50-
element={<Navigate to={SIGN_IN_PAGE} replace />}
51-
/>
52-
))}
53-
<Route path={SIGN_IN_PAGE}>
54-
<Route index element={<SignOnPage />} />
55-
<Route path='*' element={<SignOnPage />} />
56-
</Route>
57-
<Route path={SIGN_UP_PAGE}>
58-
<Route index element={<SignOnPage />} />
59-
<Route path='*' element={<SignOnPage />} />
60-
</Route>
61-
<Route path='/oauth/auth/pay' element={<OAuthPayPage />} />
62-
<Route path='/oauth/pay' element={<OAuthPayPage />} />
63-
<Route path='/oauth/auth' element={<OAuthLoginPage />} />
64-
<Route path={PRIVATE_KEY_EXPORTER_SETTINGS_PAGE}>
65-
<Route
66-
index
67-
element={
68-
<>
69-
<PrivateKeyExporterPage />
70-
<AppModal
71-
key='PrivateKeyExporter'
72-
name='PrivateKeyExporter'
73-
modal={PrivateKeyExporterModal}
74-
/>
75-
</>
76-
}
77-
/>
78-
<Route
79-
path='*'
80-
element={
81-
<>
82-
<PrivateKeyExporterPage />
83-
<AppModal
84-
key='PrivateKeyExporter'
85-
name='PrivateKeyExporter'
86-
modal={PrivateKeyExporterModal}
87-
/>
88-
</>
89-
}
90-
/>
91-
</Route>
92-
<Route
93-
path='/*'
94-
element={
95-
<AppErrorBoundary>
96-
<WebPlayer />
97-
</AppErrorBoundary>
98-
}
99-
/>
100-
</Routes>
101-
</Suspense>
102-
</AppProviders>
103-
)
6+
return <AppProviders />
1047
}

packages/web/src/app/AppProviders.tsx

Lines changed: 19 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,28 @@
1-
import { ReactNode, useState, useEffect } from 'react'
1+
import { ReactNode, useState, useMemo } from 'react'
22

3-
import { SyncLocalStorageUserProvider } from '@audius/common/api'
43
import { MediaProvider } from '@audius/harmony/src/contexts'
54
import { QueryClientProvider } from '@tanstack/react-query'
65
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
76
import { Provider as ReduxProvider } from 'react-redux'
8-
import { BrowserRouter, HashRouter, useNavigate } from 'react-router'
7+
import {
8+
createBrowserRouter,
9+
createHashRouter,
10+
RouterProvider
11+
} from 'react-router'
912
import { PersistGate } from 'redux-persist/integration/react'
1013
import { WagmiProvider } from 'wagmi'
1114

12-
import { RouterContextProvider } from 'components/animated-switch/RouterContextProvider'
13-
import { HeaderContextProvider } from 'components/header/mobile/HeaderContextProvider'
14-
import { NavProvider } from 'components/nav/mobile/NavContext'
15-
import { ScrollProvider } from 'components/scroll-provider/ScrollProvider'
16-
import { ToastContextProvider } from 'components/toast/ToastContext'
1715
import { useIsMobile } from 'hooks/useIsMobile'
18-
import { MainContentContextProvider } from 'pages/MainContentContext'
1916
import { env } from 'services/env'
20-
import { localStorage } from 'services/local-storage'
2117
import { queryClient } from 'services/query-client'
2218
import { configureStore } from 'store/configureStore'
23-
import { setNavigateRef } from 'store/navigationMiddleware'
2419
import { getSystemAppearance, getTheme } from 'utils/theme/theme'
2520

26-
import { AppContextProvider } from './AppContextProvider'
27-
import { AudiusQueryProvider } from './AudiusQueryProvider'
2821
import { wagmiAdapter } from './ReownAppKitModal'
29-
import { ThemeProvider } from './ThemeProvider'
22+
import { createRoutes } from './routes'
3023

3124
type AppProvidersProps = {
32-
children: ReactNode
25+
children?: ReactNode
3326
}
3427

3528
export const AppProviders = ({ children }: AppProvidersProps) => {
@@ -53,64 +46,27 @@ export const AppProviders = ({ children }: AppProvidersProps) => {
5346
return { store, persistor }
5447
})
5548

56-
// Use HashRouter or BrowserRouter based on environment
57-
const RouterComponent = env.USE_HASH_ROUTING ? HashRouter : BrowserRouter
5849
const basename = env.BASENAME || undefined
5950

60-
// Component to set up navigation ref for middleware
61-
const NavigationSetup = ({ children }: { children: ReactNode }) => {
62-
const navigate = useNavigate()
51+
// Create router with data router API for code-splitting and performance
52+
const router = useMemo(() => {
53+
const routes = createRoutes()
54+
const createRouter = env.USE_HASH_ROUTING
55+
? createHashRouter
56+
: createBrowserRouter
6357

64-
useEffect(() => {
65-
setNavigateRef(navigate)
66-
return () => {
67-
setNavigateRef(null as any)
68-
}
69-
}, [navigate])
70-
71-
return <>{children}</>
72-
}
73-
74-
// In React Router v7, future flags are enabled by default for declarative routers
75-
// The future prop is only available for data routers (createBrowserRouter)
76-
// For BrowserRouter/HashRouter, the v7 behavior is the default
77-
const routerProps = {
78-
basename
79-
}
58+
return createRouter(routes, {
59+
basename
60+
})
61+
}, [basename])
8062

8163
return (
8264
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
8365
<QueryClientProvider client={queryClient}>
8466
<MediaProvider>
8567
<ReduxProvider store={store}>
8668
<PersistGate loading={null} persistor={persistor}>
87-
<RouterComponent {...routerProps}>
88-
<NavigationSetup>
89-
<RouterContextProvider>
90-
<HeaderContextProvider>
91-
<NavProvider>
92-
<ScrollProvider>
93-
<ThemeProvider>
94-
<ToastContextProvider>
95-
<AppContextProvider>
96-
<AudiusQueryProvider>
97-
<MainContentContextProvider>
98-
<SyncLocalStorageUserProvider
99-
localStorage={localStorage}
100-
>
101-
{children}
102-
</SyncLocalStorageUserProvider>
103-
</MainContentContextProvider>
104-
</AudiusQueryProvider>
105-
</AppContextProvider>
106-
</ToastContextProvider>
107-
</ThemeProvider>
108-
</ScrollProvider>
109-
</NavProvider>
110-
</HeaderContextProvider>
111-
</RouterContextProvider>
112-
</NavigationSetup>
113-
</RouterComponent>
69+
<RouterProvider router={router} />
11470
</PersistGate>
11571
</ReduxProvider>
11672
</MediaProvider>

0 commit comments

Comments
 (0)