Skip to content

Commit d39dbda

Browse files
committed
fix(core): Fix initial span and orphaned span handling with useDispatchedActionData
Two fixes based on PR review feedback: 1. Allow setup/programmatic calls (no event) to create the initial navigation span even when useDispatchedActionData is enabled. The early return for actions without payload.name now checks that an event exists, so afterAllSetup and registerNavigationContainer still work correctly. 2. Pass the actual span name to ignoreEmptyRouteChangeTransactions instead of the hardcoded default. This ensures orphaned spans created with a dispatched route name are still discarded when the state listener is never called.
1 parent dcfe90c commit d39dbda

2 files changed

Lines changed: 49 additions & 7 deletions

File tree

packages/core/src/js/tracing/reactnavigation.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ export const reactNavigationIntegration = ({
379379

380380
// Extract route name from dispatch action payload when available
381381
const dispatchedRouteName = useDispatchedActionData ? getRouteNameFromAction(event) : undefined;
382-
if (useDispatchedActionData && !dispatchedRouteName && !isAppRestart) {
382+
if (useDispatchedActionData && event && !dispatchedRouteName && !isAppRestart) {
383383
debug.log(`${INTEGRATION_NAME} Navigation action has no route name in payload, not starting navigation span.`);
384384
return;
385385
}
@@ -409,12 +409,8 @@ export const reactNavigationIntegration = ({
409409
}
410410
// Always discard transactions that never receive route information
411411
const spanToCheck = latestNavigationSpan;
412-
ignoreEmptyRouteChangeTransactions(
413-
getClient(),
414-
spanToCheck,
415-
DEFAULT_NAVIGATION_SPAN_NAME,
416-
() => latestNavigationSpan === spanToCheck,
417-
);
412+
const spanName = finalSpanOptions.name ?? DEFAULT_NAVIGATION_SPAN_NAME;
413+
ignoreEmptyRouteChangeTransactions(getClient(), spanToCheck, spanName, () => latestNavigationSpan === spanToCheck);
418414

419415
if (enableTimeToInitialDisplay && latestNavigationSpan) {
420416
NATIVE.setActiveSpanId(latestNavigationSpan.spanContext().spanId);

packages/core/test/tracing/reactnavigation.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,52 @@ describe('ReactNavigationInstrumentation', () => {
10581058

10591059
expect(client.event?.transaction).toBe('TabScreen');
10601060
});
1061+
1062+
test('cancelled navigation with dispatched route name is discarded', async () => {
1063+
mockNavigation.emitWithoutStateChange({
1064+
data: {
1065+
action: {
1066+
type: 'NAVIGATE',
1067+
payload: { name: 'OrphanedScreen' },
1068+
},
1069+
noop: false,
1070+
stack: undefined,
1071+
},
1072+
});
1073+
jest.runOnlyPendingTimers(); // Trigger the timeout
1074+
1075+
await client.flush();
1076+
1077+
expect(client.event).toBeUndefined();
1078+
});
1079+
1080+
test('initial navigation span is created during setup', async () => {
1081+
// Reset and create fresh client to test initial span
1082+
const rNavigation = reactNavigationIntegration({
1083+
routeChangeTimeoutMs: 200,
1084+
useDispatchedActionData: true,
1085+
});
1086+
const freshMockNavigation = createMockNavigationAndAttachTo(rNavigation);
1087+
1088+
const rnTracing = reactNativeTracingIntegration();
1089+
const options = getDefaultTestClientOptions({
1090+
enableNativeFramesTracking: false,
1091+
enableStallTracking: false,
1092+
tracesSampleRate: 1.0,
1093+
integrations: [rNavigation, rnTracing],
1094+
enableAppStartTracking: false,
1095+
});
1096+
const freshClient = new TestClient(options);
1097+
setCurrentClient(freshClient);
1098+
freshClient.init();
1099+
1100+
jest.runOnlyPendingTimers(); // Flush the initial navigation span
1101+
1102+
await freshClient.flush();
1103+
1104+
// Initial span should be created with 'Initial Screen' from the mock container
1105+
expect(freshClient.event?.transaction).toBe('Initial Screen');
1106+
});
10611107
});
10621108

10631109
describe('useDispatchedActionData disabled (default) still uses generic span name', () => {

0 commit comments

Comments
 (0)