Skip to content

Commit b48bc08

Browse files
onurtemizkanmydea
andauthored
fix(react): Add support for basename option of createBrowserRouter (#8457)
Passes `basename` option to `matchRoutes` we use to generate branches correctly, while updating `pageload` transactions and starting `navigation` transactions. Co-authored-by: Francesco Novy <[email protected]>
1 parent dae3475 commit b48bc08

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

packages/react/src/reactrouterv6.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,15 @@ function getNormalizedName(
119119
return [location.pathname, 'url'];
120120
}
121121

122-
function updatePageloadTransaction(location: Location, routes: RouteObject[], matches?: AgnosticDataRouteMatch): void {
123-
const branches = Array.isArray(matches) ? matches : (_matchRoutes(routes, location) as unknown as RouteMatch[]);
122+
function updatePageloadTransaction(
123+
location: Location,
124+
routes: RouteObject[],
125+
matches?: AgnosticDataRouteMatch,
126+
basename?: string,
127+
): void {
128+
const branches = Array.isArray(matches)
129+
? matches
130+
: (_matchRoutes(routes, location, basename) as unknown as RouteMatch[]);
124131

125132
if (activeTransaction && branches) {
126133
activeTransaction.setName(...getNormalizedName(routes, location, branches));
@@ -132,8 +139,9 @@ function handleNavigation(
132139
routes: RouteObject[],
133140
navigationType: Action,
134141
matches?: AgnosticDataRouteMatch,
142+
basename?: string,
135143
): void {
136-
const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location);
144+
const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location, basename);
137145

138146
if (_startTransactionOnLocationChange && (navigationType === 'PUSH' || navigationType === 'POP') && branches) {
139147
if (activeTransaction) {
@@ -254,15 +262,17 @@ export function wrapCreateBrowserRouter<
254262
TRouter extends Router<TState> = Router<TState>,
255263
>(createRouterFunction: CreateRouterFunction<TState, TRouter>): CreateRouterFunction<TState, TRouter> {
256264
// `opts` for createBrowserHistory and createMemoryHistory are different, but also not relevant for us at the moment.
265+
// `basename` is the only option that is relevant for us, and it is the same for all.
257266
// eslint-disable-next-line @typescript-eslint/no-explicit-any
258-
return function (routes: RouteObject[], opts?: any): TRouter {
267+
return function (routes: RouteObject[], opts?: Record<string, any> & { basename?: string }): TRouter {
259268
const router = createRouterFunction(routes, opts);
269+
const basename = opts && opts.basename;
260270

261271
// The initial load ends when `createBrowserRouter` is called.
262272
// This is the earliest convenient time to update the transaction name.
263273
// Callbacks to `router.subscribe` are not called for the initial load.
264274
if (router.state.historyAction === 'POP' && activeTransaction) {
265-
updatePageloadTransaction(router.state.location, routes);
275+
updatePageloadTransaction(router.state.location, routes, undefined, basename);
266276
}
267277

268278
router.subscribe((state: RouterState) => {
@@ -273,7 +283,7 @@ export function wrapCreateBrowserRouter<
273283
(state.historyAction === 'PUSH' || state.historyAction === 'POP') &&
274284
activeTransaction
275285
) {
276-
handleNavigation(location, routes, state.historyAction);
286+
handleNavigation(location, routes, state.historyAction, undefined, basename);
277287
}
278288
});
279289

packages/react/src/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ export type UseNavigationType = () => Action;
7575
export type RouteObjectArrayAlias = any;
7676
export type RouteMatchAlias = any;
7777
export type CreateRoutesFromChildren = (children: JSX.Element[]) => RouteObjectArrayAlias;
78-
export type MatchRoutes = (routes: RouteObjectArrayAlias, location: Location) => RouteMatchAlias[] | null;
78+
export type MatchRoutes = (
79+
routes: RouteObjectArrayAlias,
80+
location: Location,
81+
basename?: string,
82+
) => RouteMatchAlias[] | null;
7983

8084
// Types for react-router >= 6.4.2
8185
export type ShouldRevalidateFunction = (args: any) => boolean;
@@ -203,7 +207,7 @@ export declare enum HistoryAction {
203207

204208
export interface RouterState {
205209
historyAction: Action | HistoryAction | any;
206-
location: any;
210+
location: Location;
207211
}
208212
export interface Router<TState extends RouterState = RouterState> {
209213
state: TState;

packages/react/test/reactrouterv6.4.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,5 +265,44 @@ describe('React Router v6.4', () => {
265265
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
266266
expect(mockSetName).toHaveBeenLastCalledWith('/about/:page', 'route');
267267
});
268+
269+
it('works with `basename` option', () => {
270+
const [mockStartTransaction] = createInstrumentation();
271+
const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction);
272+
273+
const router = sentryCreateBrowserRouter(
274+
[
275+
{
276+
path: '/',
277+
element: <Navigate to="/about/us" />,
278+
},
279+
{
280+
path: 'about',
281+
element: <div>About</div>,
282+
children: [
283+
{
284+
path: 'us',
285+
element: <div>Us</div>,
286+
},
287+
],
288+
},
289+
],
290+
{
291+
initialEntries: ['/app'],
292+
basename: '/app',
293+
},
294+
);
295+
296+
// @ts-ignore router is fine
297+
render(<RouterProvider router={router} />);
298+
299+
expect(mockStartTransaction).toHaveBeenCalledTimes(2);
300+
expect(mockStartTransaction).toHaveBeenLastCalledWith({
301+
name: '/app/about/us',
302+
op: 'navigation',
303+
tags: { 'routing.instrumentation': 'react-router-v6' },
304+
metadata: { source: 'url' },
305+
});
306+
});
268307
});
269308
});

0 commit comments

Comments
 (0)