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 heuristic for optimistic push #38599

Merged
merged 2 commits into from Jul 13, 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
61 changes: 55 additions & 6 deletions packages/next/client/components/reducer.ts
Expand Up @@ -149,6 +149,43 @@ const fillCacheWithDataProperty = (
)
}

const canOptimisticallyRender = (
segments: string[],
flightRouterState: FlightRouterState
): boolean => {
const segment = segments[0]
const isLastSegment = segments.length === 1

const [existingSegment, existingParallelRoutes, , , loadingMarker] =
flightRouterState

const hasLoading = loadingMarker === 'loading'

// If the tree path holds at least one loading.js it will be optimistic
if (hasLoading) {
return true
}

// Above already catches the last segment case where `hasLoading` is true, so in this case it would always be `false`.
if (isLastSegment) {
return false
}

// If the segments mismatch we can't resolve deeper into the tree
const segmentMatches = matchSegment(existingSegment, segment)

// If the existingParallelRoutes does not have a `children` parallelRouteKey we can't resolve deeper into the tree
if (!segmentMatches || !existingParallelRoutes.children) {
return hasLoading
}

// Resolve deeper in the tree as the current level did not have a loading marker
return canOptimisticallyRender(
segments.slice(1),
existingParallelRoutes.children
)
}

const createOptimisticTree = (
segments: string[],
flightRouterState: FlightRouterState | null,
Expand All @@ -163,11 +200,12 @@ const createOptimisticTree = (
const segment = segments[0]
const isLastSegment = segments.length === 1

const shouldRefetchThisLevel =
!flightRouterState || segment !== flightRouterState[0]
const segmentMatches =
existingSegment !== null && matchSegment(existingSegment, segment)
const shouldRefetchThisLevel = !flightRouterState || !segmentMatches

let parallelRoutes: FlightRouterState[1] = {}
if (existingSegment !== null && matchSegment(existingSegment, segment)) {
if (existingSegment !== null && segmentMatches) {
parallelRoutes = existingParallelRoutes
}

Expand Down Expand Up @@ -200,6 +238,11 @@ const createOptimisticTree = (
result[2] = href
}

// Copy the loading flag from existing tree
if (flightRouterState && flightRouterState[4]) {
result[4] = flightRouterState[4]
}

return result
}

Expand All @@ -215,7 +258,7 @@ const walkTreeWithFlightDataPath = (
const tree: FlightRouterState = [...treePatch]

if (url) {
tree.push(url)
tree[2] = url
}

return tree
Expand Down Expand Up @@ -246,7 +289,12 @@ const walkTreeWithFlightDataPath = (
]

if (url) {
tree.push(url)
tree[2] = url
}

// Copy loading flag
if (flightSegmentPath[4]) {
tree[4] = flightSegmentPath[4]
}

return tree
Expand Down Expand Up @@ -353,7 +401,7 @@ export function reducer(
}

// TODO-APP: flag on the tree of which part of the tree for if there is a loading boundary
const isOptimistic = false
const isOptimistic = canOptimisticallyRender(segments, state.tree)

if (isOptimistic) {
// Build optimistic tree
Expand All @@ -368,6 +416,7 @@ export function reducer(

// Fill in the cache with blank that holds the `data` field.
// TODO-APP: segments.slice(1) strips '', we can get rid of '' altogether.
cache.subTreeData = state.cache.subTreeData
const res = fillCacheWithDataProperty(
cache,
state.cache,
Expand Down
9 changes: 8 additions & 1 deletion packages/next/server/app-render.tsx
Expand Up @@ -264,7 +264,8 @@ export type FlightRouterState = [
segment: Segment,
parallelRoutes: { [parallelRouterKey: string]: FlightRouterState },
url?: string,
refresh?: 'refetch'
refresh?: 'refetch',
loading?: 'loading'
]

export type FlightSegmentPath =
Expand Down Expand Up @@ -508,7 +509,9 @@ export async function renderToHTML(
const createFlightRouterStateFromLoaderTree = ([
segment,
parallelRoutes,
{ loading },
]: LoaderTree): FlightRouterState => {
const hasLoading = Boolean(loading)
const dynamicParam = getDynamicParamFromSegment(segment)

const segmentTree: FlightRouterState = [
Expand All @@ -529,6 +532,10 @@ export async function renderToHTML(
{} as FlightRouterState[1]
)
}

if (hasLoading) {
segmentTree[4] = 'loading'
}
return segmentTree
}

Expand Down