From cbc69108ffc36dba0caed486eebb647fad6b2572 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Fri, 25 Oct 2024 21:59:06 +0200 Subject: [PATCH] Fix fetch caching inside of `"use cache"` (#71793) --- packages/next/src/server/base-server.ts | 8 +++++++- packages/next/src/server/lib/patch-fetch.ts | 8 ++++++-- .../app-dir/use-cache/app/cache-fetch/page.tsx | 18 ++++++++++++++++++ .../use-cache/app/fetch-revalidate/page.tsx | 18 ++++++++++++++++++ test/e2e/app-dir/use-cache/use-cache.test.ts | 18 ++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 test/e2e/app-dir/use-cache/app/cache-fetch/page.tsx create mode 100644 test/e2e/app-dir/use-cache/app/fetch-revalidate/page.tsx diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 9281e6344c36a..16ba857a5ca48 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -3103,9 +3103,15 @@ export default abstract class Server< ) } - return runWithCacheScope({ cache }, () => + const response = runWithCacheScope({ cache }, () => originalResponseGenerator(state) ) + + // Clear the prefetch cache to ensure a clean slate for the next + // request. + this.prefetchCacheScopesDev.del(urlPathname) + + return response } return originalResponseGenerator(state) diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index 409bebce4edfa..31c900bf82178 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -278,11 +278,15 @@ export function createPatchedFetcher( ? [] : workUnitStore.implicitTags - // Inside unstable-cache we treat it the same as force-no-store on the page. + // Inside unstable-cache or "use cache", we treat it the same as + // force-no-store on the page. const pageFetchCacheMode = - workUnitStore && workUnitStore.type === 'unstable-cache' + workUnitStore && + (workUnitStore.type === 'unstable-cache' || + workUnitStore.type === 'cache') ? 'force-no-store' : workStore.fetchCache + const isUsingNoStore = !!workStore.isUnstableNoStore let currentFetchCacheConfig = getRequestMeta('cache') diff --git a/test/e2e/app-dir/use-cache/app/cache-fetch/page.tsx b/test/e2e/app-dir/use-cache/app/cache-fetch/page.tsx new file mode 100644 index 0000000000000..1123a4cbcc8a3 --- /dev/null +++ b/test/e2e/app-dir/use-cache/app/cache-fetch/page.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +async function getData() { + 'use cache' + + return fetch('https://next-data-api-endpoint.vercel.app/api/random').then( + (res) => res.text() + ) +} + +export default async function Page() { + return ( + <> +

index page

+

{await getData()}

+ + ) +} diff --git a/test/e2e/app-dir/use-cache/app/fetch-revalidate/page.tsx b/test/e2e/app-dir/use-cache/app/fetch-revalidate/page.tsx new file mode 100644 index 0000000000000..60d5d2ba864b4 --- /dev/null +++ b/test/e2e/app-dir/use-cache/app/fetch-revalidate/page.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +async function getData() { + 'use cache' + + return fetch('https://next-data-api-endpoint.vercel.app/api/random', { + next: { revalidate: 0 }, + }).then((res) => res.text()) +} + +export default async function Page() { + return ( + <> +

index page

+

{await getData()}

+ + ) +} diff --git a/test/e2e/app-dir/use-cache/use-cache.test.ts b/test/e2e/app-dir/use-cache/use-cache.test.ts index ee20c6fcb3ed0..173ea53d19e44 100644 --- a/test/e2e/app-dir/use-cache/use-cache.test.ts +++ b/test/e2e/app-dir/use-cache/use-cache.test.ts @@ -267,6 +267,24 @@ describe('use-cache', () => { // expect(time4).toBe(time3); }) + it('should use revalidate config in fetch', async () => { + const browser = await next.browser('/fetch-revalidate') + + const initialValue = await browser.elementByCss('#random').text() + await browser.refresh() + + expect(await browser.elementByCss('#random').text()).not.toBe(initialValue) + }) + + it('should cache fetch without no-store', async () => { + const browser = await next.browser('/cache-fetch') + + const initialValue = await browser.elementByCss('#random').text() + await browser.refresh() + + expect(await browser.elementByCss('#random').text()).toBe(initialValue) + }) + it('should override fetch with no-store in use cache properly', async () => { const browser = await next.browser('/cache-fetch-no-store')