Skip to content

Commit

Permalink
fix(next): initial prefetch cache not set properly with different sea…
Browse files Browse the repository at this point in the history
…rch params (#65977)

cc @icyJoseph @ztanner

NOTE: The canary release
[`v14.1.1-canary.51`](https://github.com/vercel/next.js/releases/tag/v14.1.1-canary.51)
and below work as expected.

### Why?

Introduced from #61535, the initial prefetch cache is set based on the
`location.pathname`.
When a page is loaded WITH the search param, the cache key does not
contain information of the search param.

The issue is when on a dynamic page reading the `searchParams` value,
the value doesn't change if navigated as:

```
/?q=foo --> /
```

The prefetch cache hits, not re-rendering, and the `searchParams` value
is not passed properly.

### How?

For the prefetch cache, add the `location.search` as well.

Since `createPrefetchCacheKey` uses
[`createHrefFromUrl`](https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/router-reducer/create-href-from-url.ts)
which includes `location.search`, I'm expecting the change won't affect
current cache key behavior.

Fixes #64170
Fixes #65030

---------

Co-authored-by: Zack Tanner <[email protected]>
  • Loading branch information
devjiwonchoi and ztanner authored May 20, 2024
1 parent 8a429e0 commit 79c934a
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ export function createInitialRouterState({
// Seed the prefetch cache with this page's data.
// This is to prevent needlessly re-prefetching a page that is already reusable,
// and will avoid triggering a loading state/data fetch stall when navigating back to the page.
const url = new URL(location.pathname, location.origin)
const url = new URL(
`${location.pathname}${location.search}`,
location.origin
)

const initialFlightData: FlightData = [['', initialTree, null, null]]
createPrefetchCacheEntryForInitialLoad({
Expand Down
7 changes: 7 additions & 0 deletions test/e2e/app-dir/prefetch-searchparam/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html>
<body>{children}</body>
</html>
)
}
11 changes: 11 additions & 0 deletions test/e2e/app-dir/prefetch-searchparam/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Link from 'next/link'

export default function Page({ searchParams }: { searchParams: any }) {
return (
<>
<Link href="/">/</Link>
<Link href="/?q=bar">/?q=bar</Link>
<p>{JSON.stringify(searchParams)}</p>
</>
)
}
6 changes: 6 additions & 0 deletions test/e2e/app-dir/prefetch-searchparam/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {}

module.exports = nextConfig
25 changes: 25 additions & 0 deletions test/e2e/app-dir/prefetch-searchparam/prefetch-searchparam.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('prefetch-searchparam', () => {
const { next } = nextTestSetup({
files: __dirname,
})
it('should set prefetch cache properly on different search params', async () => {
// load WITH search param
const browser = await next.browser('/?q=foo')
expect(await browser.elementByCss('p').text()).toBe('{"q":"foo"}')

// navigate to different search param, should update the search param
await browser.elementByCss('[href="/?q=bar"]').click()
await retry(async () => {
expect(await browser.elementByCss('p').text()).toBe('{"q":"bar"}')
})

// navigate to home, should clear the searchParams value
await browser.elementByCss('[href="/"]').click()
await retry(async () => {
expect(await browser.elementByCss('p').text()).toBe('{}')
})
})
})

0 comments on commit 79c934a

Please sign in to comment.