Skip to content

Commit c8be9de

Browse files
committed
fix(router-core): separate cache identity (match.id) from lifecycle hook identity (routeId)
Cache entries use match.id (routeId + params + loaderDeps) so navigating between different params/deps correctly caches the previous match. Lifecycle hooks (onEnter/ onStay/onLeave) use routeId to track route *presence* in the matched tree — so navigating /posts/123 → /posts/456 fires onStay for /posts/$id, not onLeave+onEnter. Addresses caching regression spotted by @Sheraff in PR TanStack#6772 test.
1 parent 97ca146 commit c8be9de

1 file changed

Lines changed: 27 additions & 5 deletions

File tree

packages/router-core/src/router.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,26 +2406,48 @@ export class RouterCore<
24062406

24072407
// Commit the pending matches. If a previous match was
24082408
// removed, place it in the cachedMatches
2409+
//
2410+
// Cache identity uses match.id (routeId + params + loaderDeps) so
2411+
// navigating /foo?page=1 → /foo?page=2 correctly caches the page=1 entry.
24092412
let exitingMatches: Array<AnyRouteMatch> = []
24102413
let enteringMatches: Array<AnyRouteMatch> = []
24112414
let stayingMatches: Array<AnyRouteMatch> = []
24122415

2416+
// Lifecycle-hook identity uses routeId only so that navigating between
2417+
// different params/deps of the same route fires onStay (not onLeave+onEnter).
2418+
let hookExitingMatches: Array<AnyRouteMatch> = []
2419+
let hookEnteringMatches: Array<AnyRouteMatch> = []
2420+
let hookStayingMatches: Array<AnyRouteMatch> = []
2421+
24132422
batch(() => {
24142423
this.__store.setState((s) => {
24152424
const previousMatches = s.matches
24162425
const newMatches = s.pendingMatches || s.matches
24172426

2427+
// Cache-level identity: route id + params + loaderDeps
24182428
exitingMatches = previousMatches.filter(
2429+
(match) => !newMatches.some((d) => d.id === match.id),
2430+
)
2431+
enteringMatches = newMatches.filter(
2432+
(match) =>
2433+
!previousMatches.some((d) => d.id === match.id),
2434+
)
2435+
stayingMatches = newMatches.filter((match) =>
2436+
previousMatches.some((d) => d.id === match.id),
2437+
)
2438+
2439+
// Lifecycle-hook identity: routeId only (route presence in tree)
2440+
hookExitingMatches = previousMatches.filter(
24192441
(match) =>
24202442
!newMatches.some((d) => d.routeId === match.routeId),
24212443
)
2422-
enteringMatches = newMatches.filter(
2444+
hookEnteringMatches = newMatches.filter(
24232445
(match) =>
24242446
!previousMatches.some(
24252447
(d) => d.routeId === match.routeId,
24262448
),
24272449
)
2428-
stayingMatches = newMatches.filter((match) =>
2450+
hookStayingMatches = newMatches.filter((match) =>
24292451
previousMatches.some(
24302452
(d) => d.routeId === match.routeId,
24312453
),
@@ -2460,9 +2482,9 @@ export class RouterCore<
24602482
//
24612483
;(
24622484
[
2463-
[exitingMatches, 'onLeave'],
2464-
[enteringMatches, 'onEnter'],
2465-
[stayingMatches, 'onStay'],
2485+
[hookExitingMatches, 'onLeave'],
2486+
[hookEnteringMatches, 'onEnter'],
2487+
[hookStayingMatches, 'onStay'],
24662488
] as const
24672489
).forEach(([matches, hook]) => {
24682490
matches.forEach((match) => {

0 commit comments

Comments
 (0)