diff --git a/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx b/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx index c56e47cf5f..3c0778ef02 100644 --- a/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx +++ b/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx @@ -111,14 +111,15 @@ export async function walkTreeWithFlightRouterState({ // Explicit refresh flightRouterState[3] === 'refetch' + // Pre-PPR, the `loading` component signals to the router how deep to render the component tree + // to ensure prefetches are quick and inexpensive. If there's no `loading` component anywhere in the tree being rendered, + // the prefetch will be short-circuited to avoid requesting a potentially very expensive subtree. If there's a `loading` + // somewhere in the tree, we'll recursively render the component tree up until we encounter that loading component, and then stop. const shouldSkipComponentTree = - // loading.tsx has no effect on prefetching when PPR is enabled !experimental.isRoutePPREnabled && isPrefetch && !Boolean(components.loading) && - (flightRouterState || - // If there is no flightRouterState, we need to check the entire loader tree, as otherwise we'll be only checking the root - !hasLoadingComponentInTree(loaderTree)) + !hasLoadingComponentInTree(loaderTree) if (!parentRendered && renderComponentsOnThisLevel) { const overriddenSegment = diff --git a/test/e2e/app-dir/app-prefetch/app/page.js b/test/e2e/app-dir/app-prefetch/app/page.js index 17c9aeae53..ccded0f324 100644 --- a/test/e2e/app-dir/app-prefetch/app/page.js +++ b/test/e2e/app-dir/app-prefetch/app/page.js @@ -8,6 +8,9 @@ export default function HomePage() { To Static Page + + To Dynamic Slug Page + ) } diff --git a/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/layout.js b/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/layout.js index a9123d41fc..f404ca3e0a 100644 --- a/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/layout.js +++ b/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/layout.js @@ -2,7 +2,18 @@ import Link from 'next/link' export const dynamic = 'force-dynamic' +function getData() { + const res = new Promise((resolve) => { + setTimeout(() => { + resolve({ message: 'Layout Data!' }) + }, 2000) + }) + return res +} + export default async function Layout({ children }) { + const result = await getData() + return (

Layout

@@ -10,6 +21,7 @@ export default async function Layout({ children }) { Prefetch Link {children} +

{JSON.stringify(result)}

) } diff --git a/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/loading.js b/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/loading.js index 5b91c2379f..9105a53b67 100644 --- a/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/loading.js +++ b/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/loading.js @@ -1,3 +1,3 @@ export default function Loading() { - return

Loading Prefetch Auto

+ return

Loading Prefetch Auto

} diff --git a/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/page.js b/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/page.js index b6aa1a5033..23c8bdc9b5 100644 --- a/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/page.js +++ b/test/e2e/app-dir/app-prefetch/app/prefetch-auto/[slug]/page.js @@ -3,7 +3,7 @@ export const dynamic = 'force-dynamic' function getData() { const res = new Promise((resolve) => { setTimeout(() => { - resolve({ message: 'Hello World!' }) + resolve({ message: 'Page Data!' }) }, 2000) }) return res @@ -13,9 +13,9 @@ export default async function Page({ params }) { const result = await getData() return ( - <> +

{JSON.stringify(params)}

{JSON.stringify(result)}

- +
) } diff --git a/test/e2e/app-dir/app-prefetch/prefetching.test.ts b/test/e2e/app-dir/app-prefetch/prefetching.test.ts index 9785a4b77b..e5185013f4 100644 --- a/test/e2e/app-dir/app-prefetch/prefetching.test.ts +++ b/test/e2e/app-dir/app-prefetch/prefetching.test.ts @@ -220,7 +220,8 @@ describe('app dir - prefetching', () => { }) const prefetchResponse = await response.text() - expect(prefetchResponse).not.toContain('Hello World') + expect(prefetchResponse).toContain('Page Data!') + expect(prefetchResponse).not.toContain('Layout Data!') expect(prefetchResponse).not.toContain('Loading Prefetch Auto') }) @@ -254,7 +255,7 @@ describe('app dir - prefetching', () => { }) const prefetchResponse = await response.text() - expect(prefetchResponse).not.toContain('Hello World') + expect(prefetchResponse).not.toContain('Page Data!') expect(prefetchResponse).toContain('Loading Prefetch Auto') }) @@ -275,6 +276,19 @@ describe('app dir - prefetching', () => { ) }) + it('should immediately render the loading state for a dynamic segment when fetched from higher up in the tree', async () => { + const browser = await next.browser('/') + const loadingText = await browser + .elementById('to-dynamic-page') + .click() + .waitForElementByCss('#loading-text') + .text() + + expect(loadingText).toBe('Loading Prefetch Auto') + + await browser.waitForElementByCss('#prefetch-auto-page-data') + }) + describe('dynamic rendering', () => { describe.each(['/force-dynamic', '/revalidate-0'])('%s', (basePath) => { it('should not re-render layout when navigating between sub-pages', async () => {