Skip to content

Conversation

@onurtemizkan
Copy link
Collaborator

@onurtemizkan onurtemizkan commented Nov 27, 2025

Following up on #18098 and #18155

Fixes a race condition where rapid navigations between lazy routes cause transaction names to be incorrectly assigned or corrupted.

This occurs when async lazy handlers resolve after the user already navigated to a different route. The root cause was captureCurrentLocation() was reading window.location at the time of handler invocation, but during patchRoutesOnNavigation, the browser URL hasn't actually updated yet. So when handlers invoked during navigation A resolve later, they would capture navigation B's location (or whatever the current URL is), leading to incorrect span updates.

The fix introduces a navigation context mechanism that captures both the correct targetPath and the active span at the start of patchRoutesOnNavigation, making them available to async handler proxies during invocation. This ensures handlers always use the navigation context they were invoked with, not the current browser state.

The navigation context uses a stack to properly handle overlapping/concurrent patchRoutesOnNavigation calls. If a second navigation starts before the first one's handlers complete, each navigation maintains its own captured context.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds transaction name guards to prevent corruption when users rapidly navigate between lazy routes before lazy handlers complete resolution. The fix captures the location at the time a lazy handler is invoked and verifies it still matches the current location before updating transaction names.

  • Captures window location when lazy route handlers are invoked
  • Adds guard logic to skip span updates if user has navigated away
  • Includes comprehensive e2e tests for various rapid navigation scenarios

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react/src/reactrouter-compat-utils/lazy-routes.tsx Added captureCurrentLocation() function and modified proxy to capture location at invocation time, passing it through to processResolvedRoutes
packages/react/src/reactrouter-compat-utils/instrumentation.tsx Added guard logic to compare captured location with current window location, skipping span updates when they don't match
packages/react/test/reactrouter-compat-utils/lazy-routes.test.ts Updated test expectations to include new currentLocation parameter and added test for null-to-undefined conversion
dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/tests/transactions.test.ts Added seven new e2e tests covering rapid navigation, zero-wait navigation, browser back, and multiple overlapping handlers
dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/pages/SlowFetchLazyRoutes.tsx New test page with 500ms delay and top-level await to simulate slow lazy route loading
dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/pages/Index.tsx Added navigation link to slow-fetch route for testing
dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/pages/DelayedLazyRoute.tsx Added navigation links for testing rapid navigation between routes
dev-packages/e2e-tests/test-applications/react-router-7-lazy-routes/src/index.tsx Added slow-fetch route configuration with lazy handler

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

const windowLocation = WINDOW.location;
if (windowLocation) {
const capturedKey = computeLocationKey(currentLocation);
const currentKey = `${windowLocation.pathname}${windowLocation.search || ''}${windowLocation.hash || ''}`;
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The location key is computed twice: once using computeLocationKey() for currentLocation, and once manually for windowLocation. This duplicates the logic and increases the risk of inconsistency. Consider using computeLocationKey() for both, or extracting the manual computation into a reusable helper that accepts a window.location-like object.

Suggested change
const currentKey = `${windowLocation.pathname}${windowLocation.search || ''}${windowLocation.hash || ''}`;
const currentKey = computeLocationKey(windowLocation);

Copilot uses AI. Check for mistakes.
expect(mockProcessResolvedRoutes).toHaveBeenCalledWith(routes, route, mockLocation);
});

it('should pass null location when currentLocation is null', () => {
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name "should pass null location when currentLocation is null" is misleading because the test actually verifies that null is converted to undefined when passed to processResolvedRoutes. Consider renaming to "should convert null location to undefined for processResolvedRoutes" to better reflect what is being tested.

Suggested change
it('should pass null location when currentLocation is null', () => {
it('should convert null location to undefined for processResolvedRoutes', () => {

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

@onurtemizkan onurtemizkan force-pushed the onur/lazy-route-rapid-navigations branch 2 times, most recently from 4f05cf5 to 1deccb8 Compare November 27, 2025 17:11
@onurtemizkan onurtemizkan requested a review from Copilot November 27, 2025 17:11
Copilot finished reviewing on behalf of onurtemizkan November 27, 2025 17:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

@onurtemizkan onurtemizkan force-pushed the onur/lazy-route-rapid-navigations branch from 29a8700 to 1deccb8 Compare November 27, 2025 18:05
@onurtemizkan onurtemizkan marked this pull request as ready for review November 28, 2025 00:32
@onurtemizkan onurtemizkan force-pushed the onur/lazy-route-rapid-navigations branch from 9d875a8 to b270752 Compare November 28, 2025 12:04
@onurtemizkan onurtemizkan force-pushed the onur/lazy-route-rapid-navigations branch from b270752 to 8ae6a96 Compare November 28, 2025 12:16
@onurtemizkan onurtemizkan requested a review from s1gr1d November 28, 2025 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants