Skip to content

Commit

Permalink
app-router: update prefetch heuristic to traverse until until we hit …
Browse files Browse the repository at this point in the history
…a loading boundary (#49077)

This PR changes the router behaviour during prefetching.

- before: the router would prefetch the router tree + return the loading state of new router node if there were any
- after: the router will prefetch the router tree + prefetch the content of the new page up until the nearest loading boundaries

Example:

Note: `prefetched` here implies React content prefetching

```
.
└── app/
    ├── page.ts <- the page you're on, has links to /about and /post/[id]
    ├── layout.ts
    ├── post/
    │   └── [id]/
    │       ├── (comments)/
    │       │   ├── loading.ts <- not prefetched
    │       │   └── page.ts <- not prefetched
    │       ├── layout.ts
    │       └── page.ts <- not prefetched
    └── about/
        └── page.ts <- not prefetched
```


After
```
.
└── app/
    ├── page.ts <- the page you're on, has links to /about and /post/[id]
    ├── layout.ts
    ├── post/
    │   └── [id]/
    │       ├── (comments)/
    │       │   ├── loading.ts <- prefetched
    │       │   └── page.ts <- not prefetched
    │       ├── layout.ts
    │       └── page.ts <- prefetched
    └── about/
        └── page.ts <- prefetched
```


link NEXT-1078
  • Loading branch information
feedthejim committed May 3, 2023
1 parent 8d9658c commit c546847
Showing 1 changed file with 38 additions and 42 deletions.
80 changes: 38 additions & 42 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,8 @@ export async function renderToHTMLOrFlight(
const childSegment = parallelRoute[0]
const childSegmentParam = getDynamicParamFromSegment(childSegment)

// if we're prefetching and that there's a Loading component, we bail out
// otherwise we keep rendering for the prefetch
if (isPrefetch && Loading) {
const childProp: ChildProp = {
// Null indicates the tree is not fully rendered
Expand Down Expand Up @@ -1025,48 +1027,42 @@ export async function renderToHTMLOrFlight(
getDynamicParamFromSegment,
query
),
// Check if one level down from the common layout has a loading component. If it doesn't only provide the router state as part of the Flight data.
isPrefetch && !Boolean(components.loading)
? null
: // Create component tree using the slice of the loaderTree
// @ts-expect-error TODO-APP: fix async component type
React.createElement(async () => {
const { Component } = await createComponentTree(
// This ensures flightRouterPath is valid and filters down the tree
{
createSegmentPath,
loaderTree: loaderTreeToFilter,
parentParams: currentParams,
firstItem: isFirst,
injectedCSS,
injectedFontPreloadTags,
// This is intentionally not "rootLayoutIncludedAtThisLevelOrAbove" as createComponentTree starts at the current level and does a check for "rootLayoutAtThisLevel" too.
rootLayoutIncluded,
asNotFound,
}
)

return <Component />
}),
isPrefetch && !Boolean(components.loading)
? null
: (() => {
const { layoutOrPagePath } =
parseLoaderTree(loaderTreeToFilter)

const styles = getLayerAssets({
layoutOrPagePath,
injectedCSS: new Set(injectedCSS),
injectedFontPreloadTags: new Set(injectedFontPreloadTags),
})

return (
<>
{styles}
{rscPayloadHead}
</>
)
})(),
// Create component tree using the slice of the loaderTree
// @ts-expect-error TODO-APP: fix async component type
React.createElement(async () => {
const { Component } = await createComponentTree(
// This ensures flightRouterPath is valid and filters down the tree
{
createSegmentPath,
loaderTree: loaderTreeToFilter,
parentParams: currentParams,
firstItem: isFirst,
injectedCSS,
injectedFontPreloadTags,
// This is intentionally not "rootLayoutIncludedAtThisLevelOrAbove" as createComponentTree starts at the current level and does a check for "rootLayoutAtThisLevel" too.
rootLayoutIncluded,
asNotFound,
}
)

return <Component />
}),
(() => {
const { layoutOrPagePath } = parseLoaderTree(loaderTreeToFilter)

const styles = getLayerAssets({
layoutOrPagePath,
injectedCSS: new Set(injectedCSS),
injectedFontPreloadTags: new Set(injectedFontPreloadTags),
})

return (
<>
{styles}
{rscPayloadHead}
</>
)
})(),
],
]
}
Expand Down

0 comments on commit c546847

Please sign in to comment.