Skip to content

Commit

Permalink
fix(react): Add support for basename option of createBrowserRouter (
Browse files Browse the repository at this point in the history
#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]>
  • Loading branch information
onurtemizkan and mydea committed Jul 5, 2023
1 parent dae3475 commit b48bc08
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 8 deletions.
22 changes: 16 additions & 6 deletions packages/react/src/reactrouterv6.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,15 @@ function getNormalizedName(
return [location.pathname, 'url'];
}

function updatePageloadTransaction(location: Location, routes: RouteObject[], matches?: AgnosticDataRouteMatch): void {
const branches = Array.isArray(matches) ? matches : (_matchRoutes(routes, location) as unknown as RouteMatch[]);
function updatePageloadTransaction(
location: Location,
routes: RouteObject[],
matches?: AgnosticDataRouteMatch,
basename?: string,
): void {
const branches = Array.isArray(matches)
? matches
: (_matchRoutes(routes, location, basename) as unknown as RouteMatch[]);

if (activeTransaction && branches) {
activeTransaction.setName(...getNormalizedName(routes, location, branches));
Expand All @@ -132,8 +139,9 @@ function handleNavigation(
routes: RouteObject[],
navigationType: Action,
matches?: AgnosticDataRouteMatch,
basename?: string,
): void {
const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location);
const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location, basename);

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

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

router.subscribe((state: RouterState) => {
Expand All @@ -273,7 +283,7 @@ export function wrapCreateBrowserRouter<
(state.historyAction === 'PUSH' || state.historyAction === 'POP') &&
activeTransaction
) {
handleNavigation(location, routes, state.historyAction);
handleNavigation(location, routes, state.historyAction, undefined, basename);
}
});

Expand Down
8 changes: 6 additions & 2 deletions packages/react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ export type UseNavigationType = () => Action;
export type RouteObjectArrayAlias = any;
export type RouteMatchAlias = any;
export type CreateRoutesFromChildren = (children: JSX.Element[]) => RouteObjectArrayAlias;
export type MatchRoutes = (routes: RouteObjectArrayAlias, location: Location) => RouteMatchAlias[] | null;
export type MatchRoutes = (
routes: RouteObjectArrayAlias,
location: Location,
basename?: string,
) => RouteMatchAlias[] | null;

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

export interface RouterState {
historyAction: Action | HistoryAction | any;
location: any;
location: Location;
}
export interface Router<TState extends RouterState = RouterState> {
state: TState;
Expand Down
39 changes: 39 additions & 0 deletions packages/react/test/reactrouterv6.4.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,5 +265,44 @@ describe('React Router v6.4', () => {
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
expect(mockSetName).toHaveBeenLastCalledWith('/about/:page', 'route');
});

it('works with `basename` option', () => {
const [mockStartTransaction] = createInstrumentation();
const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction);

const router = sentryCreateBrowserRouter(
[
{
path: '/',
element: <Navigate to="/about/us" />,
},
{
path: 'about',
element: <div>About</div>,
children: [
{
path: 'us',
element: <div>Us</div>,
},
],
},
],
{
initialEntries: ['/app'],
basename: '/app',
},
);

// @ts-ignore router is fine
render(<RouterProvider router={router} />);

expect(mockStartTransaction).toHaveBeenCalledTimes(2);
expect(mockStartTransaction).toHaveBeenLastCalledWith({
name: '/app/about/us',
op: 'navigation',
tags: { 'routing.instrumentation': 'react-router-v6' },
metadata: { source: 'url' },
});
});
});
});

0 comments on commit b48bc08

Please sign in to comment.