Skip to content

Commit 65cd080

Browse files
Luca Forstneronurtemizkan
andauthored
fix(tracing): Don't finish React Router 6 pageload transactions early (#6609)
Co-authored-by: Onur Temizkan <[email protected]>
1 parent 23a7d0b commit 65cd080

File tree

1 file changed

+36
-41
lines changed

1 file changed

+36
-41
lines changed

packages/react/src/reactrouterv6.tsx

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,8 @@ function handleNavigation(
131131
location: Location,
132132
routes: RouteObject[],
133133
navigationType: Action,
134-
isBaseLocation: boolean,
135134
matches?: AgnosticDataRouteMatch,
136135
): void {
137-
if (isBaseLocation) {
138-
if (activeTransaction) {
139-
activeTransaction.finish();
140-
}
141-
142-
return;
143-
}
144-
145136
const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location);
146137

147138
if (_startTransactionOnLocationChange && (navigationType === 'PUSH' || navigationType === 'POP') && branches) {
@@ -179,28 +170,27 @@ export function withSentryReactRouterV6Routing<P extends Record<string, any>, R
179170
return Routes;
180171
}
181172

182-
let isBaseLocation: boolean = false;
183-
let routes: RouteObject[];
173+
let isMountRenderPass: boolean = true;
184174

185175
const SentryRoutes: React.FC<P> = (props: P) => {
186176
const location = _useLocation();
187177
const navigationType = _useNavigationType();
188178

189-
_useEffect(() => {
190-
// Performance concern:
191-
// This is repeated when <Routes /> is rendered.
192-
routes = _createRoutesFromChildren(props.children) as RouteObject[];
193-
isBaseLocation = true;
194-
195-
updatePageloadTransaction(location, routes);
196-
// eslint-disable-next-line react-hooks/exhaustive-deps
197-
}, [props.children]);
179+
_useEffect(
180+
() => {
181+
const routes = _createRoutesFromChildren(props.children) as RouteObject[];
198182

199-
_useEffect(() => {
200-
handleNavigation(location, routes, navigationType, isBaseLocation);
201-
}, [props.children, location, navigationType, isBaseLocation]);
202-
203-
isBaseLocation = false;
183+
if (isMountRenderPass) {
184+
updatePageloadTransaction(location, routes);
185+
isMountRenderPass = false;
186+
} else {
187+
handleNavigation(location, routes, navigationType);
188+
}
189+
},
190+
// `props.children` is purpusely not included in the dependency array, because we do not want to re-run this effect
191+
// when the children change. We only want to start transactions when the location or navigation type change.
192+
[location, navigationType],
193+
);
204194

205195
// @ts-ignore Setting more specific React Component typing for `R` generic above
206196
// will break advanced type inference done by react router params
@@ -224,28 +214,33 @@ export function wrapUseRoutes(origUseRoutes: UseRoutes): UseRoutes {
224214
return origUseRoutes;
225215
}
226216

227-
let isBaseLocation: boolean = false;
217+
let isMountRenderPass: boolean = true;
228218

229219
// eslint-disable-next-line react/display-name
230-
return (routes: RouteObject[], location?: Partial<Location> | string): React.ReactElement | null => {
231-
const SentryRoutes: React.FC<unknown> = (props: unknown) => {
232-
const Routes = origUseRoutes(routes, location);
220+
return (routes: RouteObject[], locationArg?: Partial<Location> | string): React.ReactElement | null => {
221+
const SentryRoutes: React.FC<unknown> = () => {
222+
const Routes = origUseRoutes(routes, locationArg);
233223

234-
const locationArgObject = typeof location === 'string' ? { pathname: location } : location;
235-
const locationObject = (locationArgObject as Location) || _useLocation();
224+
const location = _useLocation();
236225
const navigationType = _useNavigationType();
237226

238-
_useEffect(() => {
239-
isBaseLocation = true;
240-
241-
updatePageloadTransaction(locationObject, routes);
242-
}, [props]);
227+
// A value with stable identity to either pick `locationArg` if available or `location` if not
228+
const stableLocationParam =
229+
typeof locationArg === 'string' || locationArg?.pathname !== undefined
230+
? (locationArg as { pathname: string })
231+
: location;
243232

244233
_useEffect(() => {
245-
handleNavigation(locationObject, routes, navigationType, isBaseLocation);
246-
}, [props, locationObject, navigationType, isBaseLocation]);
247-
248-
isBaseLocation = false;
234+
const normalizedLocation =
235+
typeof stableLocationParam === 'string' ? { pathname: stableLocationParam } : stableLocationParam;
236+
237+
if (isMountRenderPass) {
238+
updatePageloadTransaction(normalizedLocation, routes);
239+
isMountRenderPass = false;
240+
} else {
241+
handleNavigation(normalizedLocation, routes, navigationType);
242+
}
243+
}, [navigationType, stableLocationParam]);
249244

250245
return Routes;
251246
};
@@ -275,7 +270,7 @@ export function wrapCreateBrowserRouter(createRouterFunction: CreateRouterFuncti
275270
(state.historyAction === 'PUSH' || state.historyAction === 'POP') &&
276271
activeTransaction
277272
) {
278-
handleNavigation(location, routes, state.historyAction, false);
273+
handleNavigation(location, routes, state.historyAction);
279274
}
280275
});
281276

0 commit comments

Comments
 (0)