Skip to content

Commit ca4fba6

Browse files
authored
fix(react): Add support for React Router sub-routes from handle (#17277)
1 parent ccc7d32 commit ca4fba6

File tree

16 files changed

+790
-2
lines changed

16 files changed

+790
-2
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*
24+
25+
/test-results/
26+
/playwright-report/
27+
/playwright/.cache/
28+
29+
!*.d.ts
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "react-router-7-lazy-routes",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@sentry/react": "latest || *",
7+
"@types/react": "18.0.0",
8+
"@types/react-dom": "18.0.0",
9+
"express": "4.20.0",
10+
"react": "18.2.0",
11+
"react-dom": "18.2.0",
12+
"react-router-dom": "7.6.0",
13+
"react-scripts": "5.0.1",
14+
"typescript": "~5.0.0"
15+
},
16+
"scripts": {
17+
"build": "react-scripts build",
18+
"start": "serve -s build",
19+
"test": "playwright test",
20+
"clean": "npx rimraf node_modules pnpm-lock.yaml",
21+
"test:build": "pnpm install && npx playwright install && pnpm build",
22+
"test:build-ts3.8": "pnpm install && pnpm add [email protected] && npx playwright install && pnpm build",
23+
"test:build-canary": "pnpm install && pnpm add react@canary react-dom@canary && npx playwright install && pnpm build",
24+
"test:assert": "pnpm test"
25+
},
26+
"eslintConfig": {
27+
"extends": [
28+
"react-app",
29+
"react-app/jest"
30+
]
31+
},
32+
"browserslist": {
33+
"production": [
34+
">0.2%",
35+
"not dead",
36+
"not op_mini all"
37+
],
38+
"development": [
39+
"last 1 chrome version",
40+
"last 1 firefox version",
41+
"last 1 safari version"
42+
]
43+
},
44+
"devDependencies": {
45+
"@playwright/test": "~1.53.2",
46+
"@sentry-internal/test-utils": "link:../../../test-utils",
47+
"serve": "14.0.1",
48+
"npm-run-all2": "^6.2.0"
49+
},
50+
"volta": {
51+
"extends": "../../package.json"
52+
}
53+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const config = getPlaywrightConfig({
4+
startCommand: `pnpm start`,
5+
});
6+
7+
export default config;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<meta name="theme-color" content="#000000" />
7+
<meta name="description" content="Web site created using create-react-app" />
8+
<title>React App</title>
9+
</head>
10+
<body>
11+
<noscript>You need to enable JavaScript to run this app.</noscript>
12+
<div id="root"></div>
13+
<!--
14+
This HTML file is a template.
15+
If you open it directly in the browser, you will see an empty page.
16+
17+
You can add webfonts, meta tags, or analytics to this file.
18+
The build step will place the bundled scripts into the <body> tag.
19+
20+
To begin the development, run `npm start` or `yarn start`.
21+
To create a production bundle, use `npm run build` or `yarn build`.
22+
-->
23+
</body>
24+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface Window {
2+
recordedTransactions?: string[];
3+
capturedExceptionId?: string;
4+
sentryReplayId?: string;
5+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as Sentry from '@sentry/react';
2+
import React from 'react';
3+
import ReactDOM from 'react-dom/client';
4+
import {
5+
Navigate,
6+
PatchRoutesOnNavigationFunction,
7+
RouterProvider,
8+
createBrowserRouter,
9+
createRoutesFromChildren,
10+
matchRoutes,
11+
useLocation,
12+
useNavigationType,
13+
} from 'react-router-dom';
14+
import Index from './pages/Index';
15+
16+
17+
Sentry.init({
18+
environment: 'qa', // dynamic sampling bias to keep transactions
19+
dsn: process.env.REACT_APP_E2E_TEST_DSN,
20+
integrations: [
21+
Sentry.reactRouterV7BrowserTracingIntegration({
22+
useEffect: React.useEffect,
23+
useLocation,
24+
useNavigationType,
25+
createRoutesFromChildren,
26+
matchRoutes,
27+
trackFetchStreamPerformance: true,
28+
enableAsyncRouteHandlers: true,
29+
}),
30+
],
31+
// We recommend adjusting this value in production, or using tracesSampler
32+
// for finer control
33+
tracesSampleRate: 1.0,
34+
release: 'e2e-test',
35+
36+
tunnel: 'http://localhost:3031',
37+
});
38+
39+
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter);
40+
41+
const router = sentryCreateBrowserRouter(
42+
[
43+
{
44+
path: '/',
45+
element: <Index />,
46+
},
47+
{
48+
path: '/lazy',
49+
handle: {
50+
lazyChildren: () => import('./pages/InnerLazyRoutes').then(module => module.someMoreNestedRoutes),
51+
},
52+
},
53+
{
54+
path: '/static',
55+
element: <>Hello World</>,
56+
},
57+
{
58+
path: '*',
59+
element: <Navigate to="/" replace />,
60+
},
61+
],
62+
{
63+
async patchRoutesOnNavigation({ matches, patch }: Parameters<PatchRoutesOnNavigationFunction>[0]) {
64+
const leafRoute = matches[matches.length - 1]?.route;
65+
if (leafRoute?.id && leafRoute?.handle?.lazyChildren) {
66+
const children = await leafRoute.handle.lazyChildren();
67+
patch(leafRoute.id, children);
68+
}
69+
},
70+
},
71+
);
72+
73+
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
74+
root.render(<RouterProvider router={router} />);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as React from 'react';
2+
import { Link } from 'react-router-dom';
3+
4+
const Index = () => {
5+
return (
6+
<>
7+
<Link to="/lazy/inner/123/456/789" id="navigation">
8+
navigate
9+
</Link>
10+
</>
11+
);
12+
};
13+
14+
export default Index;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
3+
export const someMoreNestedRoutes = [
4+
{
5+
path: 'inner',
6+
children: [
7+
{
8+
index: true,
9+
element: <>Level 1</>,
10+
},
11+
{
12+
path: ':id',
13+
children: [
14+
{
15+
index: true,
16+
element: <>Level 1 ID</>,
17+
},
18+
{
19+
path: ':anotherId',
20+
children: [
21+
{
22+
index: true,
23+
element: <>Level 1 ID Another ID</>,
24+
},
25+
{
26+
path: ':someAnotherId',
27+
element: <div id="innermost-lazy-route">
28+
Rendered
29+
</div>,
30+
},
31+
],
32+
},
33+
],
34+
},
35+
],
36+
},
37+
];
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="react-scripts" />

0 commit comments

Comments
 (0)