Skip to content

Commit

Permalink
fix navigation applying stale data when triggered from global not found
Browse files Browse the repository at this point in the history
  • Loading branch information
ztanner committed Feb 14, 2024
1 parent 39deaa5 commit fc01c8e
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 0 deletions.
10 changes: 10 additions & 0 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,16 @@ async function ReactServerApp({ tree, ctx, asNotFound }: ReactServerAppProps) {
query
)

// If the page we're rendering is being treated as the global not-found page, we want to special-case
// the segment key so it doesn't collide with a page matching the same path.
// This is necessary because when rendering the global not-found, it will always be the root segment.
// If the not-found page prefetched a link to the root page, it would have the same data path
// (e.g., ['', { children: ['__PAGE__', {}] }]). Without this disambiguation, the router would interpret
// these pages as being able to share the same cache nodes, which is not the case as they render different things.
if (asNotFound) {
initialTree[0] = '__NOT_FOUND__'
}

const [MetadataTree, MetadataOutlet] = createMetadataComponents({
tree,
errorType: asNotFound ? 'not-found' : undefined,
Expand Down
19 changes: 19 additions & 0 deletions test/e2e/app-dir/prefetching-not-found/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// we want the layout to opt-out of static prefetching
export const dynamic = 'force-dynamic'

import Link from 'next/link'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<Link href="/">Link to `/`</Link>
<div>{children}</div>
</body>
</html>
)
}
10 changes: 10 additions & 0 deletions test/e2e/app-dir/prefetching-not-found/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Link from 'next/link'

export default function Home() {
return (
<div>
<h1>Home Page</h1>
<Link href="/fake-link">Go to Invalid Page</Link>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('prefetching-not-found', () => {
const { next } = nextTestSetup({
files: __dirname,
})

it('should correctly navigate to/from a global 404 page when following links with prefetch=auto', async () => {
let browser = await next.browser('/')
expect(await browser.elementByCss('h1').text()).toBe('Home Page')

await browser.elementByCss("[href='/fake-link']").click()

await retry(async () => {
expect(await browser.elementByCss('body').text()).toContain(
'This page could not be found.'
)
})

await browser.elementByCss("[href='/']").click()

await retry(async () => {
expect(await browser.elementByCss('h1').text()).toBe('Home Page')
})

// assert the same behavior, but starting at the not found page. This is to ensure that when we seed the prefetch cache,
// we don't have any cache collisions that would cause the not-found page to remain rendered when following a link to the home page
browser = await next.browser('/fake-link')
expect(await browser.elementByCss('body').text()).toContain(
'This page could not be found.'
)

await browser.elementByCss("[href='/']").click()

await retry(async () => {
expect(await browser.elementByCss('h1').text()).toBe('Home Page')
})
})
})

0 comments on commit fc01c8e

Please sign in to comment.