Skip to content

fix(react): Add support for basename option of createBrowserRouter #8457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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' },
});
});
});
});