Skip to content

Commit e479bde

Browse files
committed
feat(react): Use initialEntries in wrapped routers.
1 parent dc08b82 commit e479bde

File tree

2 files changed

+116
-5
lines changed

2 files changed

+116
-5
lines changed

packages/react/src/reactrouterv6-compat-utils.tsx

+27-5
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,39 @@ export function createV6CompatibleWrapCreateBrowserRouter<
8383
// `opts` for createBrowserHistory and createMemoryHistory are different, but also not relevant for us at the moment.
8484
// `basename` is the only option that is relevant for us, and it is the same for all.
8585
// eslint-disable-next-line @typescript-eslint/no-explicit-any
86-
return function (routes: RouteObject[], opts?: Record<string, any> & { basename?: string }): TRouter {
86+
return function (
87+
routes: RouteObject[],
88+
opts?: Record<string, any> & {
89+
basename?: string;
90+
initialEntries?: (string | { pathname: string })[];
91+
initialIndex?: number;
92+
},
93+
): TRouter {
8794
const router = createRouterFunction(routes, opts);
8895
const basename = opts && opts.basename;
8996

9097
const activeRootSpan = getActiveRootSpan();
9198

92-
// The initial load ends when `createBrowserRouter` is called.
93-
// This is the earliest convenient time to update the transaction name.
94-
// Callbacks to `router.subscribe` are not called for the initial load.
99+
const initialEntries = opts && opts.initialEntries;
100+
const initialIndex = opts && opts.initialIndex;
101+
102+
const hasOnlyOneInitialEntry = initialEntries && initialEntries.length === 1;
103+
const hasIndexedEntry = initialIndex !== undefined && initialEntries && initialEntries[initialIndex];
104+
105+
const initialEntry = hasOnlyOneInitialEntry
106+
? initialEntries[0]
107+
: hasIndexedEntry
108+
? initialEntries[initialIndex]
109+
: undefined;
110+
111+
const location = initialEntry
112+
? typeof initialEntry === 'string'
113+
? { pathname: initialEntry }
114+
: initialEntry
115+
: router.state.location;
116+
95117
if (router.state.historyAction === 'POP' && activeRootSpan) {
96-
updatePageloadTransaction(activeRootSpan, router.state.location, routes, undefined, basename);
118+
updatePageloadTransaction(activeRootSpan, location, routes, undefined, basename);
97119
}
98120

99121
router.subscribe((state: RouterState) => {

packages/react/test/reactrouterv6.test.tsx

+89
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ import {
1313
Navigate,
1414
Outlet,
1515
Route,
16+
RouterProvider,
1617
Routes,
18+
createMemoryRouter,
1719
createRoutesFromChildren,
1820
matchRoutes,
1921
useLocation,
2022
useNavigationType,
2123
useRoutes,
2224
} from 'react-router-6';
2325

26+
import type { RouteObject } from 'react-router-6';
27+
2428
import { BrowserClient } from '../src';
2529
import {
2630
reactRouterV6BrowserTracingIntegration,
2731
withSentryReactRouterV6Routing,
32+
wrapCreateBrowserRouterV6,
2833
wrapUseRoutesV6,
2934
} from '../src/reactrouterv6';
3035

@@ -79,6 +84,90 @@ describe('reactRouterV6BrowserTracingIntegration', () => {
7984
getCurrentScope().setClient(undefined);
8085
});
8186

87+
describe('wrapCreateBrowserRouterV6 - createMemoryRouter', () => {
88+
it('starts a pageload transaction', () => {
89+
const client = createMockBrowserClient();
90+
setCurrentClient(client);
91+
92+
client.addIntegration(
93+
reactRouterV6BrowserTracingIntegration({
94+
useEffect: React.useEffect,
95+
useLocation,
96+
useNavigationType,
97+
createRoutesFromChildren,
98+
matchRoutes,
99+
}),
100+
);
101+
102+
const routes: RouteObject[] = [
103+
{
104+
path: '/',
105+
element: <div>Home</div>,
106+
},
107+
{
108+
path: '/about',
109+
element: <div>About</div>,
110+
},
111+
];
112+
113+
const wrappedCreateMemoryRouter = wrapCreateBrowserRouterV6(createMemoryRouter);
114+
115+
const router = wrappedCreateMemoryRouter(routes, {
116+
initialEntries: ['/', '/about'],
117+
initialIndex: 1,
118+
});
119+
120+
render(<RouterProvider router={router} />);
121+
122+
expect(mockStartBrowserTracingPageLoadSpan).toHaveBeenCalledTimes(1);
123+
expect(mockStartBrowserTracingPageLoadSpan).toHaveBeenLastCalledWith(expect.any(BrowserClient), {
124+
name: '/',
125+
attributes: {
126+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
127+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
128+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v6',
129+
},
130+
});
131+
});
132+
133+
it('updates the transaction name on a pageload', () => {
134+
const client = createMockBrowserClient();
135+
setCurrentClient(client);
136+
137+
client.addIntegration(
138+
reactRouterV6BrowserTracingIntegration({
139+
useEffect: React.useEffect,
140+
useLocation,
141+
useNavigationType,
142+
createRoutesFromChildren,
143+
matchRoutes,
144+
}),
145+
);
146+
147+
const routes: RouteObject[] = [
148+
{
149+
path: '/',
150+
element: <div>Home</div>,
151+
},
152+
{
153+
path: '/about',
154+
element: <div>About</div>,
155+
},
156+
];
157+
158+
const wrappedCreateMemoryRouter = wrapCreateBrowserRouterV6(createMemoryRouter);
159+
160+
const router = wrappedCreateMemoryRouter(routes, {
161+
initialEntries: ['/', '/about'],
162+
initialIndex: 2,
163+
});
164+
165+
render(<RouterProvider router={router} />);
166+
167+
expect(mockRootSpan.updateName).toHaveBeenLastCalledWith('/about');
168+
});
169+
});
170+
82171
describe('withSentryReactRouterV6Routing', () => {
83172
it('starts a pageload transaction', () => {
84173
const client = createMockBrowserClient();

0 commit comments

Comments
 (0)