Skip to content

Commit 19a8e81

Browse files
committed
Pass RouteTree into navigation function
RouteTree is the client version of FlightRouterState. It's the same representation of the route tree, but it structured for optimized lookups of the client cache. The plan is the make this the primary/only type used for dealing with routes on the client; FlightRouterState will be used a transport format only.
1 parent 28d237c commit 19a8e81

File tree

12 files changed

+480
-452
lines changed

12 files changed

+480
-452
lines changed

packages/next/src/client/components/router-reducer/create-initial-router-state.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { extractPathFromFlightRouterState } from './compute-changed-path'
66
import type { AppRouterState } from './router-reducer-types'
77
import { getFlightDataPartsFromPath } from '../../flight-data-helpers'
88
import { createInitialCacheNodeForHydration } from './ppr-navigations'
9+
import { convertRootFlightRouterStateToRouteTree } from '../segment-cache/cache'
10+
import type { NormalizedSearch } from '../segment-cache/cache-key'
911

1012
export interface InitialRouterStateParameters {
1113
navigatedAt: number
@@ -44,11 +46,22 @@ export function createInitialRouterState({
4446
createHrefFromUrl(location)
4547
: initialCanonicalUrl
4648

49+
// Conver the initial FlightRouterState into the RouteTree type.
50+
// NOTE: The metadataVaryPath isn't used for anything currently because the
51+
// head is embedded into the CacheNode tree, but eventually we'll lift it out
52+
// and store it on the top-level state object.
53+
const acc = { metadataVaryPath: null }
54+
const initialRouteTree = convertRootFlightRouterStateToRouteTree(
55+
initialTree,
56+
initialRenderedSearch as NormalizedSearch,
57+
acc
58+
)
59+
4760
const initialState = {
4861
tree: initialTree,
4962
cache: createInitialCacheNodeForHydration(
5063
navigatedAt,
51-
initialTree,
64+
initialRouteTree,
5265
initialSeedData,
5366
initialHead
5467
),

packages/next/src/client/components/router-reducer/fetch-server-response.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@ import {
3535
} from '../../flight-data-helpers'
3636
import { getAppBuildId } from '../../app-build-id'
3737
import { setCacheBustingSearchParam } from './set-cache-busting-search-param'
38-
import {
39-
getRenderedSearch,
40-
urlToUrlWithoutFlightMarker,
41-
} from '../../route-params'
38+
import { urlToUrlWithoutFlightMarker } from '../../route-params'
4239
import type { NormalizedSearch } from '../segment-cache/cache-key'
4340
import { getDeploymentId } from '../../../shared/lib/deployment-id'
4441

@@ -256,7 +253,14 @@ export async function fetchServerResponse(
256253
return {
257254
flightData: normalizedFlightData,
258255
canonicalUrl: canonicalUrl,
259-
renderedSearch: getRenderedSearch(res),
256+
// TODO: We should be able to read this from the rewrite header, not the
257+
// Flight response. Theoretically they should always agree, but there are
258+
// currently some cases where it's incorrect for interception routes. We
259+
// can always trust the value in the response body. However, per-segment
260+
// prefetch responses don't embed the value in the body; they rely on the
261+
// header alone. So we need to investigate why the header is sometimes
262+
// wrong for interception routes.
263+
renderedSearch: flightResponse.q as NormalizedSearch,
260264
couldBeIntercepted: interception,
261265
prerendered: flightResponse.S,
262266
postponed,

packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.test.ts

Lines changed: 0 additions & 100 deletions
This file was deleted.

packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import type { FlightRouterState } from '../../../shared/lib/app-router-types'
2+
import type { RouteTree } from '../segment-cache/cache'
23

34
export function isNavigatingToNewRootLayout(
45
currentTree: FlightRouterState,
5-
nextTree: FlightRouterState
6+
nextTree: RouteTree
67
): boolean {
78
// Compare segments
89
const currentTreeSegment = currentTree[0]
9-
const nextTreeSegment = nextTree[0]
10+
const nextTreeSegment = nextTree.segment
1011

1112
// If any segment is different before we find the root layout, the root layout has changed.
1213
// E.g. /same/(group1)/layout.js -> /same/(group2)/layout.js
@@ -27,17 +28,26 @@ export function isNavigatingToNewRootLayout(
2728
// Current tree root layout found
2829
if (currentTree[4]) {
2930
// If the next tree doesn't have the root layout flag, it must have changed.
30-
return !nextTree[4]
31+
return !nextTree.isRootLayout
3132
}
3233
// Current tree didn't have its root layout here, must have changed.
33-
if (nextTree[4]) {
34+
if (nextTree.isRootLayout) {
3435
return true
3536
}
36-
// We can't assume it's `parallelRoutes.children` here in case the root layout is `app/@something/layout.js`
37-
// But it's not possible to be more than one parallelRoutes before the root layout is found
38-
// TODO-APP: change to traverse all parallel routes
39-
const currentTreeChild = Object.values(currentTree[1])[0]
40-
const nextTreeChild = Object.values(nextTree[1])[0]
41-
if (!currentTreeChild || !nextTreeChild) return true
42-
return isNavigatingToNewRootLayout(currentTreeChild, nextTreeChild)
37+
38+
const slots = nextTree.slots
39+
const currentTreeChildren = currentTree[1]
40+
if (slots !== null) {
41+
for (const slot in slots) {
42+
const nextTreeChild = slots[slot]
43+
const currentTreeChild = currentTreeChildren[slot]
44+
if (
45+
currentTreeChild === undefined ||
46+
isNavigatingToNewRootLayout(currentTreeChild, nextTreeChild)
47+
) {
48+
return true
49+
}
50+
}
51+
}
52+
return true
4353
}

0 commit comments

Comments
 (0)