From 79c934aaecae4acac6b5a635b9cf3bb95265a945 Mon Sep 17 00:00:00 2001 From: Jiwon Choi Date: Tue, 21 May 2024 00:44:03 +0900 Subject: [PATCH] fix(next): initial prefetch cache not set properly with different search 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 <1939140+ztanner@users.noreply.github.com> --- .../create-initial-router-state.ts | 5 +++- .../prefetch-searchparam/app/layout.tsx | 7 ++++++ .../app-dir/prefetch-searchparam/app/page.tsx | 11 ++++++++ .../prefetch-searchparam/next.config.js | 6 +++++ .../prefetch-searchparam.test.ts | 25 +++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/e2e/app-dir/prefetch-searchparam/app/layout.tsx create mode 100644 test/e2e/app-dir/prefetch-searchparam/app/page.tsx create mode 100644 test/e2e/app-dir/prefetch-searchparam/next.config.js create mode 100644 test/e2e/app-dir/prefetch-searchparam/prefetch-searchparam.test.ts diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts index 9a8b5bc901dc6..e3a2cbcdd832e 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts @@ -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({ diff --git a/test/e2e/app-dir/prefetch-searchparam/app/layout.tsx b/test/e2e/app-dir/prefetch-searchparam/app/layout.tsx new file mode 100644 index 0000000000000..e7077399c03ce --- /dev/null +++ b/test/e2e/app-dir/prefetch-searchparam/app/layout.tsx @@ -0,0 +1,7 @@ +export default function Root({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/prefetch-searchparam/app/page.tsx b/test/e2e/app-dir/prefetch-searchparam/app/page.tsx new file mode 100644 index 0000000000000..932fdc612a540 --- /dev/null +++ b/test/e2e/app-dir/prefetch-searchparam/app/page.tsx @@ -0,0 +1,11 @@ +import Link from 'next/link' + +export default function Page({ searchParams }: { searchParams: any }) { + return ( + <> + / + /?q=bar +

{JSON.stringify(searchParams)}

+ + ) +} diff --git a/test/e2e/app-dir/prefetch-searchparam/next.config.js b/test/e2e/app-dir/prefetch-searchparam/next.config.js new file mode 100644 index 0000000000000..807126e4cf0bf --- /dev/null +++ b/test/e2e/app-dir/prefetch-searchparam/next.config.js @@ -0,0 +1,6 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/test/e2e/app-dir/prefetch-searchparam/prefetch-searchparam.test.ts b/test/e2e/app-dir/prefetch-searchparam/prefetch-searchparam.test.ts new file mode 100644 index 0000000000000..594c690aa5d32 --- /dev/null +++ b/test/e2e/app-dir/prefetch-searchparam/prefetch-searchparam.test.ts @@ -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('{}') + }) + }) +})