Fix fetchCache and no-store handling (#49638)
Follow-up to https://github.com/vercel/next.js/pull/49628 this updates the `export const fetchCache` handling and `cache: 'no-store'` handling as discussed which also aligns with our docs here https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#fetchcache - `export const fetchCache = 'force-cache'`: forces all fetches to be cached regardless of `cache: 'no-store'` but cacheable `revalidate` values still take priority - `export const fetchCache = 'default-cache'`: ensures fetches are cached even if they come after a `cache: 'no-store'` fetch but don't override `cache: 'no-store'` itself. - without `export const fetchCache`, we still disable fetch cache for successive fetches after a fetch that does `cache: 'no-store'` x-ref: [slack thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1683732826894469)
This commit is contained in:
parent
f3222471c6
commit
2eeb1c1bd1
6 changed files with 327 additions and 11 deletions
|
@ -15,6 +15,7 @@ export interface StaticGenerationStore {
|
||||||
fetchCache?:
|
fetchCache?:
|
||||||
| 'only-cache'
|
| 'only-cache'
|
||||||
| 'force-cache'
|
| 'force-cache'
|
||||||
|
| 'default-cache'
|
||||||
| 'force-no-store'
|
| 'force-no-store'
|
||||||
| 'default-no-store'
|
| 'default-no-store'
|
||||||
| 'only-no-store'
|
| 'only-no-store'
|
||||||
|
|
|
@ -32,6 +32,7 @@ function trackFetchMetric(
|
||||||
url: string
|
url: string
|
||||||
status: number
|
status: number
|
||||||
method: string
|
method: string
|
||||||
|
cacheReason: string
|
||||||
cacheStatus: 'hit' | 'miss'
|
cacheStatus: 'hit' | 'miss'
|
||||||
start: number
|
start: number
|
||||||
}
|
}
|
||||||
|
@ -158,6 +159,8 @@ export function patchFetch({
|
||||||
}
|
}
|
||||||
const isOnlyCache = staticGenerationStore.fetchCache === 'only-cache'
|
const isOnlyCache = staticGenerationStore.fetchCache === 'only-cache'
|
||||||
const isForceCache = staticGenerationStore.fetchCache === 'force-cache'
|
const isForceCache = staticGenerationStore.fetchCache === 'force-cache'
|
||||||
|
const isDefaultCache =
|
||||||
|
staticGenerationStore.fetchCache === 'default-cache'
|
||||||
const isDefaultNoStore =
|
const isDefaultNoStore =
|
||||||
staticGenerationStore.fetchCache === 'default-no-store'
|
staticGenerationStore.fetchCache === 'default-no-store'
|
||||||
const isOnlyNoStore =
|
const isOnlyNoStore =
|
||||||
|
@ -165,9 +168,19 @@ export function patchFetch({
|
||||||
const isForceNoStore =
|
const isForceNoStore =
|
||||||
staticGenerationStore.fetchCache === 'force-no-store'
|
staticGenerationStore.fetchCache === 'force-no-store'
|
||||||
|
|
||||||
const _cache = getRequestMeta('cache')
|
let _cache = getRequestMeta('cache')
|
||||||
|
|
||||||
if (_cache === 'force-cache' || isForceCache) {
|
if (
|
||||||
|
typeof _cache === 'string' &&
|
||||||
|
typeof curRevalidate !== 'undefined'
|
||||||
|
) {
|
||||||
|
console.warn(
|
||||||
|
`Warning: fetch for ${input.toString()} specified "cache: ${_cache}" and "revalidate: ${curRevalidate}", only one should be specified.`
|
||||||
|
)
|
||||||
|
_cache = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cache === 'force-cache') {
|
||||||
curRevalidate = false
|
curRevalidate = false
|
||||||
}
|
}
|
||||||
if (['no-cache', 'no-store'].includes(_cache || '')) {
|
if (['no-cache', 'no-store'].includes(_cache || '')) {
|
||||||
|
@ -177,6 +190,7 @@ export function patchFetch({
|
||||||
revalidate = curRevalidate
|
revalidate = curRevalidate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cacheReason = ''
|
||||||
const _headers = getRequestMeta('headers')
|
const _headers = getRequestMeta('headers')
|
||||||
const initHeaders: Headers =
|
const initHeaders: Headers =
|
||||||
typeof _headers?.get === 'function'
|
typeof _headers?.get === 'function'
|
||||||
|
@ -199,6 +213,7 @@ export function patchFetch({
|
||||||
|
|
||||||
if (isForceNoStore) {
|
if (isForceNoStore) {
|
||||||
revalidate = 0
|
revalidate = 0
|
||||||
|
cacheReason = 'fetchCache = force-no-store'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOnlyNoStore) {
|
if (isOnlyNoStore) {
|
||||||
|
@ -208,26 +223,43 @@ export function patchFetch({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
revalidate = 0
|
revalidate = 0
|
||||||
|
cacheReason = 'fetchCache = only-no-store'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOnlyCache && _cache === 'no-store') {
|
||||||
|
throw new Error(
|
||||||
|
`cache: 'no-store' used on fetch for ${input.toString()} with 'export const fetchCache = 'only-cache'`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isForceCache &&
|
||||||
|
(typeof curRevalidate === 'undefined' || curRevalidate === 0)
|
||||||
|
) {
|
||||||
|
cacheReason = 'fetchCache = force-cache'
|
||||||
|
revalidate = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof revalidate === 'undefined') {
|
if (typeof revalidate === 'undefined') {
|
||||||
if (isOnlyCache && _cache === 'no-store') {
|
if (isDefaultCache) {
|
||||||
throw new Error(
|
revalidate = false
|
||||||
`cache: 'no-store' used on fetch for ${input.toString()} with 'export const fetchCache = 'only-cache'`
|
cacheReason = 'fetchCache = default-cache'
|
||||||
)
|
} else if (autoNoCache) {
|
||||||
}
|
|
||||||
|
|
||||||
if (autoNoCache) {
|
|
||||||
revalidate = 0
|
revalidate = 0
|
||||||
|
cacheReason = 'auto no cache'
|
||||||
} else if (isDefaultNoStore) {
|
} else if (isDefaultNoStore) {
|
||||||
revalidate = 0
|
revalidate = 0
|
||||||
|
cacheReason = 'fetchCache = default-no-store'
|
||||||
} else {
|
} else {
|
||||||
|
cacheReason = 'auto cache'
|
||||||
revalidate =
|
revalidate =
|
||||||
typeof staticGenerationStore.revalidate === 'boolean' ||
|
typeof staticGenerationStore.revalidate === 'boolean' ||
|
||||||
typeof staticGenerationStore.revalidate === 'undefined'
|
typeof staticGenerationStore.revalidate === 'undefined'
|
||||||
? false
|
? false
|
||||||
: staticGenerationStore.revalidate
|
: staticGenerationStore.revalidate
|
||||||
}
|
}
|
||||||
|
} else if (!cacheReason) {
|
||||||
|
cacheReason = `revalidate: ${revalidate}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -316,6 +348,7 @@ export function patchFetch({
|
||||||
trackFetchMetric(staticGenerationStore, {
|
trackFetchMetric(staticGenerationStore, {
|
||||||
start: fetchStart,
|
start: fetchStart,
|
||||||
url: fetchUrl,
|
url: fetchUrl,
|
||||||
|
cacheReason,
|
||||||
cacheStatus: 'miss',
|
cacheStatus: 'miss',
|
||||||
status: res.status,
|
status: res.status,
|
||||||
method: clonedInit.method || 'GET',
|
method: clonedInit.method || 'GET',
|
||||||
|
@ -422,6 +455,7 @@ export function patchFetch({
|
||||||
trackFetchMetric(staticGenerationStore, {
|
trackFetchMetric(staticGenerationStore, {
|
||||||
start: fetchStart,
|
start: fetchStart,
|
||||||
url: fetchUrl,
|
url: fetchUrl,
|
||||||
|
cacheReason,
|
||||||
cacheStatus: 'hit',
|
cacheStatus: 'hit',
|
||||||
status: resData.status || 200,
|
status: resData.status || 200,
|
||||||
method: init?.method || 'GET',
|
method: init?.method || 'GET',
|
||||||
|
|
|
@ -69,8 +69,12 @@ createNextDescribe(
|
||||||
curData = JSON.parse(cur$('#props').text())
|
curData = JSON.parse(cur$('#props').text())
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(curData.data.random).toBeTruthy()
|
try {
|
||||||
expect(curData.data.random).toBe(prevData.data.random)
|
expect(curData.data.random).toBeTruthy()
|
||||||
|
expect(curData.data.random).toBe(prevData.data.random)
|
||||||
|
} finally {
|
||||||
|
prevData = curData
|
||||||
|
}
|
||||||
return 'success'
|
return 'success'
|
||||||
}, 'success')
|
}, 'success')
|
||||||
})
|
})
|
||||||
|
@ -437,10 +441,15 @@ createNextDescribe(
|
||||||
'blog/tim.rsc',
|
'blog/tim.rsc',
|
||||||
'blog/tim/first-post.html',
|
'blog/tim/first-post.html',
|
||||||
'blog/tim/first-post.rsc',
|
'blog/tim/first-post.rsc',
|
||||||
|
'default-cache/page.js',
|
||||||
'dynamic-error/[id]/page.js',
|
'dynamic-error/[id]/page.js',
|
||||||
'dynamic-no-gen-params-ssr/[slug]/page.js',
|
'dynamic-no-gen-params-ssr/[slug]/page.js',
|
||||||
'dynamic-no-gen-params/[slug]/page.js',
|
'dynamic-no-gen-params/[slug]/page.js',
|
||||||
|
'fetch-no-cache/page.js',
|
||||||
'flight/[slug]/[slug2]/page.js',
|
'flight/[slug]/[slug2]/page.js',
|
||||||
|
'force-cache.html',
|
||||||
|
'force-cache.rsc',
|
||||||
|
'force-cache/page.js',
|
||||||
'force-dynamic-catch-all/[slug]/[[...id]]/page.js',
|
'force-dynamic-catch-all/[slug]/[[...id]]/page.js',
|
||||||
'force-dynamic-no-prerender/[id]/page.js',
|
'force-dynamic-no-prerender/[id]/page.js',
|
||||||
'force-dynamic-prerender/[slug]/page.js',
|
'force-dynamic-prerender/[slug]/page.js',
|
||||||
|
@ -618,6 +627,11 @@ createNextDescribe(
|
||||||
srcRoute: '/blog/[author]/[slug]',
|
srcRoute: '/blog/[author]/[slug]',
|
||||||
dataRoute: '/blog/styfle/second-post.rsc',
|
dataRoute: '/blog/styfle/second-post.rsc',
|
||||||
},
|
},
|
||||||
|
'/force-cache': {
|
||||||
|
dataRoute: '/force-cache.rsc',
|
||||||
|
initialRevalidateSeconds: 3,
|
||||||
|
srcRoute: '/force-cache',
|
||||||
|
},
|
||||||
'/hooks/use-pathname/slug': {
|
'/hooks/use-pathname/slug': {
|
||||||
dataRoute: '/hooks/use-pathname/slug.rsc',
|
dataRoute: '/hooks/use-pathname/slug.rsc',
|
||||||
initialRevalidateSeconds: false,
|
initialRevalidateSeconds: false,
|
||||||
|
@ -880,6 +894,116 @@ createNextDescribe(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('should cache correctly for fetchCache = default-cache', async () => {
|
||||||
|
const res = await next.fetch('/default-cache')
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
|
||||||
|
let prevHtml = await res.text()
|
||||||
|
let prev$ = cheerio.load(prevHtml)
|
||||||
|
|
||||||
|
await check(async () => {
|
||||||
|
const curRes = await next.fetch('/default-cache')
|
||||||
|
expect(curRes.status).toBe(200)
|
||||||
|
|
||||||
|
const curHtml = await curRes.text()
|
||||||
|
const cur$ = cheerio.load(curHtml)
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(cur$('#data-no-cache').text()).not.toBe(
|
||||||
|
prev$('#data-no-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-force-cache').text()).toBe(
|
||||||
|
prev$('#data-force-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-and-fetch-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-and-fetch-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-and-fetch-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-and-fetch-cache').text()
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
prevHtml = curHtml
|
||||||
|
prev$ = cur$
|
||||||
|
}
|
||||||
|
return 'success'
|
||||||
|
}, 'success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should cache correctly for fetchCache = force-cache', async () => {
|
||||||
|
const res = await next.fetch('/force-cache')
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
|
||||||
|
let prevHtml = await res.text()
|
||||||
|
let prev$ = cheerio.load(prevHtml)
|
||||||
|
|
||||||
|
await check(async () => {
|
||||||
|
const curRes = await next.fetch('/force-cache')
|
||||||
|
expect(curRes.status).toBe(200)
|
||||||
|
|
||||||
|
const curHtml = await curRes.text()
|
||||||
|
const cur$ = cheerio.load(curHtml)
|
||||||
|
|
||||||
|
expect(cur$('#data-no-cache').text()).toBe(
|
||||||
|
prev$('#data-no-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-force-cache').text()).toBe(
|
||||||
|
prev$('#data-force-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-and-fetch-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-and-fetch-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-auto-cache').text()).toBe(
|
||||||
|
prev$('#data-auto-cache').text()
|
||||||
|
)
|
||||||
|
|
||||||
|
return 'success'
|
||||||
|
}, 'success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should cache correctly for cache: no-store', async () => {
|
||||||
|
const res = await next.fetch('/fetch-no-cache')
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
|
||||||
|
let prevHtml = await res.text()
|
||||||
|
let prev$ = cheerio.load(prevHtml)
|
||||||
|
|
||||||
|
await check(async () => {
|
||||||
|
const curRes = await next.fetch('/fetch-no-cache')
|
||||||
|
expect(curRes.status).toBe(200)
|
||||||
|
|
||||||
|
const curHtml = await curRes.text()
|
||||||
|
const cur$ = cheerio.load(curHtml)
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(cur$('#data-no-cache').text()).not.toBe(
|
||||||
|
prev$('#data-no-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-force-cache').text()).toBe(
|
||||||
|
prev$('#data-force-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-revalidate-and-fetch-cache').text()).toBe(
|
||||||
|
prev$('#data-revalidate-and-fetch-cache').text()
|
||||||
|
)
|
||||||
|
expect(cur$('#data-auto-cache').text()).not.toBe(
|
||||||
|
prev$('#data-auto-cache').text()
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
prevHtml = curHtml
|
||||||
|
prev$ = cur$
|
||||||
|
}
|
||||||
|
return 'success'
|
||||||
|
}, 'success')
|
||||||
|
})
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
it('should bypass fetch cache with cache-control: no-cache', async () => {
|
it('should bypass fetch cache with cache-control: no-cache', async () => {
|
||||||
const res = await fetchViaHTTP(
|
const res = await fetchViaHTTP(
|
||||||
|
|
53
test/e2e/app-dir/app-static/app/default-cache/page.js
Normal file
53
test/e2e/app-dir/app-static/app/default-cache/page.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
export const fetchCache = 'default-cache'
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const dataNoCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?a1',
|
||||||
|
{
|
||||||
|
cache: 'no-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataForceCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?b2',
|
||||||
|
{
|
||||||
|
cache: 'force-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataRevalidateCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?c3',
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
revalidate: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataRevalidateAndFetchCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?d4',
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
revalidate: 3,
|
||||||
|
},
|
||||||
|
cache: 'force-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataAutoCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?d4'
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>/force-cache</p>
|
||||||
|
<p id="data-no-cache">"cache: no-cache" {dataNoCache}</p>
|
||||||
|
<p id="data-force-cache">"cache: force-cache" {dataForceCache}</p>
|
||||||
|
<p id="data-revalidate-cache">"revalidate: 3" {dataRevalidateCache}</p>
|
||||||
|
<p id="data-revalidate-and-fetch-cache">
|
||||||
|
"revalidate: 3 and cache: force-cache" {dataRevalidateAndFetchCache}
|
||||||
|
</p>
|
||||||
|
<p id="data-auto-cache">"auto cache" {dataAutoCache}</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
51
test/e2e/app-dir/app-static/app/fetch-no-cache/page.js
Normal file
51
test/e2e/app-dir/app-static/app/fetch-no-cache/page.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
export default async function Page() {
|
||||||
|
const dataNoCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?a2',
|
||||||
|
{
|
||||||
|
cache: 'no-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataForceCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?b3',
|
||||||
|
{
|
||||||
|
cache: 'force-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataRevalidateCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?c4',
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
revalidate: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataRevalidateAndFetchCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?d5',
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
revalidate: 3,
|
||||||
|
},
|
||||||
|
cache: 'force-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataAutoCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?d6'
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>/fetch-no-cache</p>
|
||||||
|
<p id="data-no-cache">"cache: no-cache" {dataNoCache}</p>
|
||||||
|
<p id="data-force-cache">"cache: force-cache" {dataForceCache}</p>
|
||||||
|
<p id="data-revalidate-cache">"revalidate: 3" {dataRevalidateCache}</p>
|
||||||
|
<p id="data-revalidate-and-fetch-cache">
|
||||||
|
"revalidate: 3 and cache: force-cache" {dataRevalidateAndFetchCache}
|
||||||
|
</p>
|
||||||
|
<p id="data-auto-cache">"auto cache" {dataAutoCache}</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
53
test/e2e/app-dir/app-static/app/force-cache/page.js
Normal file
53
test/e2e/app-dir/app-static/app/force-cache/page.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
export const fetchCache = 'force-cache'
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const dataNoCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?a1',
|
||||||
|
{
|
||||||
|
cache: 'no-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataForceCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?b2',
|
||||||
|
{
|
||||||
|
cache: 'force-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataRevalidateCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?c3',
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
revalidate: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataRevalidateAndFetchCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?d4',
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
revalidate: 3,
|
||||||
|
},
|
||||||
|
cache: 'force-cache',
|
||||||
|
}
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
const dataAutoCache = await fetch(
|
||||||
|
'https://next-data-api-endpoint.vercel.app/api/random?d4'
|
||||||
|
).then((res) => res.text())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>/force-cache</p>
|
||||||
|
<p id="data-no-cache">"cache: no-cache" {dataNoCache}</p>
|
||||||
|
<p id="data-force-cache">"cache: force-cache" {dataForceCache}</p>
|
||||||
|
<p id="data-revalidate-cache">"revalidate: 3" {dataRevalidateCache}</p>
|
||||||
|
<p id="data-revalidate-and-fetch-cache">
|
||||||
|
"revalidate: 3 and cache: force-cache" {dataRevalidateAndFetchCache}
|
||||||
|
</p>
|
||||||
|
<p id="data-auto-cache">"auto cache" {dataAutoCache}</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in a new issue