Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional comments for reducer/cachenode #39065

Merged
merged 3 commits into from Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
87 changes: 63 additions & 24 deletions packages/next/client/components/reducer.ts
Expand Up @@ -7,6 +7,9 @@ import type {
import { matchSegment } from './match-segments'
import { fetchServerResponse } from './app-router.client'

/**
* Fill cache with subTreeData based on flightDataPath
*/
function fillCacheWithNewSubTreeData(
newCache: CacheNode,
existingCache: CacheNode,
Expand Down Expand Up @@ -73,6 +76,9 @@ function fillCacheWithNewSubTreeData(
)
}

/**
* Kick off fetch based on the common layout between two routes. Fill cache with data property holding the in-progress fetch.
*/
function fillCacheWithDataProperty(
newCache: CacheNode,
existingCache: CacheNode,
Expand Down Expand Up @@ -148,6 +154,10 @@ function fillCacheWithDataProperty(
)
}

/**
* Decide if the segments can be optimistically rendered, kicking off the fetch in layout-router.
* - When somewhere in the path to the segment there is a loading.js this becomes true
*/
function canOptimisticallyRender(
segments: string[],
flightRouterState: FlightRouterState
Expand Down Expand Up @@ -186,6 +196,10 @@ function canOptimisticallyRender(
)
}

/**
* Create optimistic version of router state based on the existing router state and segments.
* This is used to allow rendering layout-routers up till the point where data is missing.
*/
function createOptimisticTree(
segments: string[],
flightRouterState: FlightRouterState | null,
Expand Down Expand Up @@ -246,7 +260,10 @@ function createOptimisticTree(
return result
}

function walkTreeWithFlightDataPath(
/**
* Apply the router state from the Flight response. Creates a new router state tree.
*/
function applyRouterStatePatchToTree(
flightSegmentPath: FlightData[0],
flightRouterState: FlightRouterState,
treePatch: FlightRouterState
Expand Down Expand Up @@ -279,7 +296,7 @@ function walkTreeWithFlightDataPath(
...parallelRoutes,
[parallelRouteKey]: lastSegment
? treePatch
: walkTreeWithFlightDataPath(
: applyRouterStatePatchToTree(
flightSegmentPath.slice(2),
parallelRoutes[parallelRouteKey],
treePatch
Expand All @@ -299,32 +316,13 @@ function walkTreeWithFlightDataPath(
return tree
}

type PushRef = {
/**
* If the app-router should push a new history entry in app-router's useEffect()
*/
pendingPush: boolean
/**
* Multi-page navigation through location.href.
*/
mpaNavigation: boolean
}

export type FocusAndScrollRef = {
/**
* If focus and scroll should be set in the layout-router's useEffect()
*/
apply: boolean
}

type AppRouterState = {
tree: FlightRouterState
cache: CacheNode
pushRef: PushRef
focusAndScrollRef: FocusAndScrollRef
canonicalUrl: string
}

export const ACTION_RELOAD = 'reload'
export const ACTION_NAVIGATE = 'navigate'
export const ACTION_RESTORE = 'restore'
Expand Down Expand Up @@ -407,6 +405,47 @@ interface ServerPatchAction {
cache: CacheNode
}

interface PushRef {
/**
* If the app-router should push a new history entry in app-router's useEffect()
*/
pendingPush: boolean
/**
* Multi-page navigation through location.href.
*/
mpaNavigation: boolean
}

/**
* Handles keeping the state of app-router.
*/
type AppRouterState = {
/**
* The router state, this is written into the history state in app-router using replaceState/pushState.
* - Has to be serializable as it is written into the history state.
* - Holds which segments are shown on the screen.
* - Holds where loading states (loading.js) exists.
*/
tree: FlightRouterState
/**
* The cache holds React nodes for every segment that is shown on screen as well as previously shown segments and prefetched segments.
* It also holds in-progress data requests.
*/
cache: CacheNode
/**
* Decides if the update should create a new history entry and if the navigation can't be handled by app-router.
*/
pushRef: PushRef
/**
* Decides if the update should apply scroll and focus management.
*/
focusAndScrollRef: FocusAndScrollRef
/**
* The canonical url that is pushed/replaced
*/
canonicalUrl: string
}

/**
* Reducer that handles the app-router state updates.
*/
Expand Down Expand Up @@ -575,7 +614,7 @@ export function reducer(
const flightSegmentPath = flightDataPath.slice(0, -3)

// Create new tree based on the flightSegmentPath and router state patch
const newTree = walkTreeWithFlightDataPath(
const newTree = applyRouterStatePatchToTree(
// TODO-APP: remove ''
['', ...flightSegmentPath],
state.tree,
Expand Down Expand Up @@ -641,7 +680,7 @@ export function reducer(
const treePath = flightDataPath.slice(0, -3)
const [treePatch] = flightDataPath.slice(-2)

const newTree = walkTreeWithFlightDataPath(
const newTree = applyRouterStatePatchToTree(
// TODO-APP: remove ''
['', ...treePath],
state.tree,
Expand Down Expand Up @@ -726,7 +765,7 @@ export function reducer(

// Given the path can only have two items the items are only the router state and subTreeData for the root.
const [treePatch, subTreeData] = flightDataPath
const newTree = walkTreeWithFlightDataPath(
const newTree = applyRouterStatePatchToTree(
// TODO-APP: remove ''
[''],
state.tree,
Expand Down
38 changes: 29 additions & 9 deletions packages/next/server/app-render.tsx
Expand Up @@ -246,6 +246,9 @@ type DynamicParamTypes = 'catchall' | 'optional-catchall' | 'dynamic'
// d = dynamic
export type DynamicParamTypesShort = 'c' | 'oc' | 'd'

/**
* Shorten the dynamic param in order to make it smaller when transmitted to the browser.
*/
function getShortDynamicParamType(
type: DynamicParamTypes
): DynamicParamTypesShort {
Expand All @@ -261,10 +264,16 @@ function getShortDynamicParamType(
}
}

/**
* Segment in the router state.
*/
export type Segment =
| string
| [param: string, value: string, type: DynamicParamTypesShort]

/**
* LoaderTree is generated in next-app-loader.
*/
type LoaderTree = [
segment: string,
parallelRoutes: { [parallelRouterKey: string]: LoaderTree },
Expand All @@ -275,6 +284,9 @@ type LoaderTree = [
}
]

/**
* Router state
*/
export type FlightRouterState = [
segment: Segment,
parallelRoutes: { [parallelRouterKey: string]: FlightRouterState },
Expand All @@ -283,7 +295,11 @@ export type FlightRouterState = [
loading?: 'loading'
]

/**
* Individual Flight response path
*/
export type FlightSegmentPath =
// Uses `any` as repeating pattern can't be typed.
| any[]
// Looks somewhat like this
| [
Expand All @@ -296,21 +312,25 @@ export type FlightSegmentPath =
]

export type FlightDataPath =
// Uses `any` as repeating pattern can't be typed.
| any[]
// Looks somewhat like this
| [
segment: Segment,
parallelRoute: string,
segment: Segment,
parallelRoute: string,
segment: Segment,
parallelRoute: string,
currentSegment: Segment,
tree: FlightRouterState,
subTreeData: React.ReactNode
// Holds full path to the segment.
...FlightSegmentPath,
/* segment of the rendered slice: */ Segment,
/* treePatch */ FlightRouterState,
/* subTreeData: */ React.ReactNode
]

/**
* The Flight response data
*/
export type FlightData = Array<FlightDataPath> | string

/**
* Property holding the current subTreeData.
*/
export type ChildProp = {
current: React.ReactNode
segment: Segment
Expand Down
14 changes: 13 additions & 1 deletion packages/next/shared/lib/app-router-context.ts
Expand Up @@ -4,11 +4,23 @@ import type { FlightRouterState, FlightData } from '../../server/app-render'

export type ChildSegmentMap = Map<string, CacheNode>

/**
* Cache node used in app-router / layout-router.
*/
export type CacheNode = {
/**
* In-flight request for this node.
*/
data: ReturnType<
typeof import('../../client/components/app-router.client').fetchServerResponse
> | null
subTreeData: null | React.ReactNode
/**
* React Component for this node.
*/
subTreeData: React.ReactNode | null
/**
* Child parallel routes.
*/
parallelRoutes: Map<string, ChildSegmentMap>
}

Expand Down