From 243ac8d9cc7bee18e78e4ccf6624273b7dffa8b4 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 23 Oct 2024 14:00:03 -0700 Subject: [PATCH] Fix fetch with no-store inside of use cache --- packages/next/src/server/lib/patch-fetch.ts | 24 +++++++++++-------- .../app/cache-fetch-no-store/page.tsx | 18 ++++++++++++++ test/e2e/app-dir/use-cache/use-cache.test.ts | 9 +++++++ 3 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 test/e2e/app-dir/use-cache/app/cache-fetch-no-store/page.tsx diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index 8e7db1e569e41..409bebce4edfa 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -304,16 +304,20 @@ export function createPatchedFetcher( if (currentFetchCacheConfig === 'force-cache') { currentFetchRevalidate = false } else if ( - currentFetchCacheConfig === 'no-cache' || - currentFetchCacheConfig === 'no-store' || - pageFetchCacheMode === 'force-no-store' || - pageFetchCacheMode === 'only-no-store' || - // If no explicit fetch cache mode is set, but dynamic = `force-dynamic` is set, - // we shouldn't consider caching the fetch. This is because the `dynamic` cache - // is considered a "top-level" cache mode, whereas something like `fetchCache` is more - // fine-grained. Top-level modes are responsible for setting reasonable defaults for the - // other configurations. - (!pageFetchCacheMode && workStore.forceDynamic) + // if we are inside of "use cache"/"unstable_cache" + // we shouldn't set the revalidate to 0 as it's overridden + // by the cache context + workUnitStore?.type !== 'cache' && + (currentFetchCacheConfig === 'no-cache' || + currentFetchCacheConfig === 'no-store' || + pageFetchCacheMode === 'force-no-store' || + pageFetchCacheMode === 'only-no-store' || + // If no explicit fetch cache mode is set, but dynamic = `force-dynamic` is set, + // we shouldn't consider caching the fetch. This is because the `dynamic` cache + // is considered a "top-level" cache mode, whereas something like `fetchCache` is more + // fine-grained. Top-level modes are responsible for setting reasonable defaults for the + // other configurations. + (!pageFetchCacheMode && workStore.forceDynamic)) ) { currentFetchRevalidate = 0 } diff --git a/test/e2e/app-dir/use-cache/app/cache-fetch-no-store/page.tsx b/test/e2e/app-dir/use-cache/app/cache-fetch-no-store/page.tsx new file mode 100644 index 0000000000000..a56172b09c7c3 --- /dev/null +++ b/test/e2e/app-dir/use-cache/app/cache-fetch-no-store/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', { + cache: 'no-store', + }).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 606423f4b4184..68f9e3de002e6 100644 --- a/test/e2e/app-dir/use-cache/use-cache.test.ts +++ b/test/e2e/app-dir/use-cache/use-cache.test.ts @@ -266,4 +266,13 @@ describe('use-cache', () => { // const time4 = await browser.waitForElementByCss('#t').text() // expect(time4).toBe(time3); }) + + it('should override fetch with no-store in use cache properly', async () => { + const browser = await next.browser('/cache-fetch-no-store') + + const initialValue = await browser.elementByCss('#random').text() + await browser.refresh() + + expect(await browser.elementByCss('#random').text()).toBe(initialValue) + }) })