Skip to content

Commit 4f93036

Browse files
authored
Fix hash issue for matchRoutes optimization (#13108)
1 parent b39b749 commit 4f93036

File tree

3 files changed

+64
-17
lines changed

3 files changed

+64
-17
lines changed

.changeset/witty-birds-fetch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Fix regression introduced in `6.29.0` via [#12169](https://github.com/remix-run/react-router/pull/12169) that caused issues navigating to hash routes inside splat routes for applications using Lazy Route Discovery (`patchRoutesOnNavigation`)

packages/router/__tests__/navigation-test.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { HydrationState } from "../index";
2-
import { json } from "../index";
2+
import { createMemoryHistory, createRouter, json } from "../index";
33
import { cleanup, setup } from "./utils/data-router-setup";
44
import { createFormData } from "./utils/utils";
55

@@ -434,6 +434,47 @@ describe("navigations", () => {
434434
});
435435
});
436436

437+
it("does not use fog of war partial matches for hash change only navigations", async () => {
438+
let router = createRouter({
439+
history: createMemoryHistory(),
440+
routes: [
441+
{
442+
path: "/",
443+
children: [
444+
{
445+
path: "*",
446+
},
447+
],
448+
},
449+
],
450+
// This is what enables the partialMatches logic
451+
patchRoutesOnNavigation: () => {},
452+
}).initialize();
453+
expect(router.state.location).toMatchObject({
454+
pathname: "/",
455+
hash: "",
456+
});
457+
expect(router.state.matches).toMatchObject([{ route: { path: "/" } }]);
458+
await router.navigate("/foo");
459+
expect(router.state.location).toMatchObject({
460+
pathname: "/foo",
461+
hash: "",
462+
});
463+
expect(router.state.matches).toMatchObject([
464+
{ route: { path: "/" } },
465+
{ route: { path: "*" } },
466+
]);
467+
await router.navigate("/foo#bar");
468+
expect(router.state.location).toMatchObject({
469+
pathname: "/foo",
470+
hash: "#bar",
471+
});
472+
expect(router.state.matches).toMatchObject([
473+
{ route: { path: "/" } },
474+
{ route: { path: "*" } },
475+
]);
476+
});
477+
437478
it("redirects from loaders (throw)", async () => {
438479
let t = initializeTest();
439480

packages/router/router.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,23 @@ export function createRouter(init: RouterInit): Router {
15341534
: matchRoutes(routesToUse, location, basename);
15351535
let flushSync = (opts && opts.flushSync) === true;
15361536

1537+
// Short circuit if it's only a hash change and not a revalidation or
1538+
// mutation submission.
1539+
//
1540+
// Ignore on initial page loads because since the initial hydration will always
1541+
// be "same hash". For example, on /page#hash and submit a <Form method="post">
1542+
// which will default to a navigation to /page
1543+
if (
1544+
matches &&
1545+
state.initialized &&
1546+
!isRevalidationRequired &&
1547+
isHashChangeOnly(state.location, location) &&
1548+
!(opts && opts.submission && isMutationMethod(opts.submission.formMethod))
1549+
) {
1550+
completeNavigation(location, { matches }, { flushSync });
1551+
return;
1552+
}
1553+
15371554
let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
15381555
if (fogOfWar.active && fogOfWar.matches) {
15391556
matches = fogOfWar.matches;
@@ -1558,22 +1575,6 @@ export function createRouter(init: RouterInit): Router {
15581575
return;
15591576
}
15601577

1561-
// Short circuit if it's only a hash change and not a revalidation or
1562-
// mutation submission.
1563-
//
1564-
// Ignore on initial page loads because since the initial hydration will always
1565-
// be "same hash". For example, on /page#hash and submit a <Form method="post">
1566-
// which will default to a navigation to /page
1567-
if (
1568-
state.initialized &&
1569-
!isRevalidationRequired &&
1570-
isHashChangeOnly(state.location, location) &&
1571-
!(opts && opts.submission && isMutationMethod(opts.submission.formMethod))
1572-
) {
1573-
completeNavigation(location, { matches }, { flushSync });
1574-
return;
1575-
}
1576-
15771578
// Create a controller/Request for this navigation
15781579
pendingNavigationController = new AbortController();
15791580
let request = createClientSideRequest(

0 commit comments

Comments
 (0)