Skip to content

Commit d9d1115

Browse files
authored
fix(react): Support root and wildcard routes in react router v6 (#5971)
1. Makes sure that routes don't appear with double slashes, like with `//example` or `//my-route`. This is done with a simple check for `pathBuilder[pathBuilder.length - 1] === '/'`. 2. Make sure that wildcard imports will generate proper transaction names. For example with `/tests/123` that loads a wildcard route, render `/tests/:testId/*`. Previously with wildcard routes we would only generate the wildcard portion, so it would render `:testId/*`.
1 parent 12a4297 commit d9d1115

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

packages/react/src/reactrouterv6.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,17 @@ function getNormalizedName(
122122

123123
const path = route.path;
124124
if (path) {
125-
const newPath = path[0] === '/' ? path : `/${path}`;
125+
const newPath = path[0] === '/' || pathBuilder[pathBuilder.length - 1] === '/' ? path : `/${path}`;
126126
pathBuilder += newPath;
127127
if (branch.pathname === location.pathname) {
128-
// If the route defined on the element is something like
129-
// <Route path="/stores/:storeId/products/:productId" element={<div>Product</div>} />
130-
// We should check against the branch.pathname for the number of / seperators
131-
if (getNumberOfUrlSegments(pathBuilder) !== getNumberOfUrlSegments(branch.pathname)) {
128+
if (
129+
// If the route defined on the element is something like
130+
// <Route path="/stores/:storeId/products/:productId" element={<div>Product</div>} />
131+
// We should check against the branch.pathname for the number of / seperators
132+
getNumberOfUrlSegments(pathBuilder) !== getNumberOfUrlSegments(branch.pathname) &&
133+
// We should not count wildcard operators in the url segments calculation
134+
pathBuilder.slice(-2) !== '/*'
135+
) {
132136
return [newPath, 'route'];
133137
}
134138
return [pathBuilder, 'route'];

packages/react/test/reactrouterv6.test.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
matchRoutes,
77
MemoryRouter,
88
Navigate,
9+
Outlet,
910
Route,
1011
Routes,
1112
useLocation,
@@ -525,5 +526,96 @@ describe('React Router v6', () => {
525526
metadata: { source: 'route' },
526527
});
527528
});
529+
530+
it('does not add double slashes to URLS', () => {
531+
const [mockStartTransaction, { mockSetName }] = createInstrumentation();
532+
const wrappedUseRoutes = wrapUseRoutes(useRoutes);
533+
534+
const Routes = () =>
535+
wrappedUseRoutes([
536+
{
537+
path: '/',
538+
element: (
539+
<div>
540+
<Outlet />
541+
</div>
542+
),
543+
children: [
544+
{
545+
path: 'tests',
546+
children: [
547+
{ index: true, element: <div>Main Test</div> },
548+
{ path: ':testId/*', element: <div>Test Component</div> },
549+
],
550+
},
551+
{ path: '/', element: <Navigate to="/home" /> },
552+
{ path: '*', element: <Navigate to="/404" replace /> },
553+
],
554+
},
555+
{
556+
path: '/',
557+
element: <div />,
558+
children: [
559+
{ path: '404', element: <div>Error</div> },
560+
{ path: '*', element: <Navigate to="/404" replace /> },
561+
],
562+
},
563+
]);
564+
565+
render(
566+
<MemoryRouter initialEntries={['/tests']}>
567+
<Routes />
568+
</MemoryRouter>,
569+
);
570+
571+
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
572+
// should be /tests not //tests
573+
expect(mockSetName).toHaveBeenLastCalledWith('/tests', 'route');
574+
});
575+
576+
it('handles wildcard routes properly', () => {
577+
const [mockStartTransaction, { mockSetName }] = createInstrumentation();
578+
const wrappedUseRoutes = wrapUseRoutes(useRoutes);
579+
580+
const Routes = () =>
581+
wrappedUseRoutes([
582+
{
583+
path: '/',
584+
element: (
585+
<div>
586+
<Outlet />
587+
</div>
588+
),
589+
children: [
590+
{
591+
path: 'tests',
592+
children: [
593+
{ index: true, element: <div>Main Test</div> },
594+
{ path: ':testId/*', element: <div>Test Component</div> },
595+
],
596+
},
597+
{ path: '/', element: <Navigate to="/home" /> },
598+
{ path: '*', element: <Navigate to="/404" replace /> },
599+
],
600+
},
601+
{
602+
path: '/',
603+
element: <div />,
604+
children: [
605+
{ path: '404', element: <div>Error</div> },
606+
{ path: '*', element: <Navigate to="/404" replace /> },
607+
],
608+
},
609+
]);
610+
611+
render(
612+
<MemoryRouter initialEntries={['/tests/123']}>
613+
<Routes />
614+
</MemoryRouter>,
615+
);
616+
617+
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
618+
expect(mockSetName).toHaveBeenLastCalledWith('/tests/:testId/*', 'route');
619+
});
528620
});
529621
});

0 commit comments

Comments
 (0)