Update docs for revalidatePath fix (#55083)
This updates docs for the fixes landed in https://github.com/vercel/next.js/pull/53321 related to `revalidatePath`. Fixes: https://github.com/vercel/next.js/issues/49387 --------- Co-authored-by: Lee Robinson <me@leerob.io>
This commit is contained in:
parent
45fbd4db14
commit
c2f587640a
7 changed files with 99 additions and 15 deletions
|
@ -98,7 +98,7 @@ Set the cache lifetime of a resource (in seconds).
|
|||
fetch(`https://...`, { next: { tags: ['collection'] } })
|
||||
```
|
||||
|
||||
Set the cache tags of a resource. Data can then be revalidated on-demand using [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag).
|
||||
Set the cache tags of a resource. Data can then be revalidated on-demand using [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag). The max length for a custom tag is 256 characters.
|
||||
|
||||
## Version History
|
||||
|
||||
|
|
|
@ -8,16 +8,16 @@ description: API Reference for the revalidatePath function.
|
|||
> **Good to know**:
|
||||
>
|
||||
> - `revalidatePath` is available in both [Node.js and Edge runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes).
|
||||
> - `revalidatePath` will revalidate _all segments_ under a dynamic route segment. For example, if you have a dynamic segment `/product/[id]` and you call `revalidatePath('/product/[id]')`, then all segments under `/product/[id]` will be revalidated as requested.
|
||||
> - `revalidatePath` only invalidates the cache when the path is next visited. This means calling `revalidatePath` with a dynamic route segment will not immediately trigger many revalidations at once. The invalidation only happens when the path is next visited.
|
||||
> - `revalidatePath` only invalidates the cache when the included path is next visited. This means calling `revalidatePath` with a dynamic route segment will not immediately trigger many revalidations at once. The invalidation only happens when the path is next visited.
|
||||
|
||||
## Parameters
|
||||
|
||||
```tsx
|
||||
revalidatePath(path: string): void;
|
||||
revalidatePath(path: string, type?: 'page' | 'layout'): void;
|
||||
```
|
||||
|
||||
- `path`: A string representing the filesystem path associated with the data you want to revalidate. This is **not** the literal route segment (e.g. `/product/123`) but instead the path on the filesystem (e.g. `/product/[id]`).
|
||||
- `path`: A string representing the filesystem path associated with the data you want to revalidate. This is the literal route segment (for example, `/product/123`) not the path on the filesystem `/product/[slug]/page`. Must be less than 1024 characters.
|
||||
- `type`: (optional) `'page'` or `'layout'` string to change the type of path to revalidate.
|
||||
|
||||
## Returns
|
||||
|
||||
|
@ -25,6 +25,33 @@ revalidatePath(path: string): void;
|
|||
|
||||
## Examples
|
||||
|
||||
### Revalidating A Specific URL
|
||||
|
||||
```ts
|
||||
import { revalidatePath } from 'next/cache'
|
||||
revalidatePath('/blog/post-1')
|
||||
```
|
||||
|
||||
This will revalidate one specific URL on the next page visit.
|
||||
|
||||
### Revalidating A Page Path
|
||||
|
||||
```ts
|
||||
import { revalidatePath } from 'next/cache'
|
||||
revalidatePath('/blog/[slug]', 'page')
|
||||
```
|
||||
|
||||
This will revalidate any URL that matches the provided `page` file on the next page visit. This will _not_ invalidate pages beneath the specific page. For example, `/blog/[slug]` won't invalidate `/blog/[slug]/[author]`.
|
||||
|
||||
### Revalidating A Layout Path
|
||||
|
||||
```ts
|
||||
import { revalidatePath } from 'next/cache'
|
||||
revalidatePath('/blog/[slug]', 'layout')
|
||||
```
|
||||
|
||||
This will revalidate any URL that matches the provided `layout` file on the next page visit. This will cause pages beneath with the same layout to revalidate on the next visit. For example, in the above case, `/blog/[slug]/[another]` would also revalidate on the next visit.
|
||||
|
||||
### Server Action
|
||||
|
||||
```ts filename="app/actions.ts" switcher
|
||||
|
|
|
@ -16,7 +16,7 @@ description: API Reference for the revalidateTag function.
|
|||
revalidateTag(tag: string): void;
|
||||
```
|
||||
|
||||
- `tag`: A string representing the cache tag associated with the data you want to revalidate.
|
||||
- `tag`: A string representing the cache tag associated with the data you want to revalidate. Must be less than or equal to 256 characters.
|
||||
|
||||
You can add tags to `fetch` as follows:
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ export const NEXT_CACHE_REVALIDATED_TAGS_HEADER = 'x-next-revalidated-tags'
|
|||
export const NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER =
|
||||
'x-next-revalidate-tag-token'
|
||||
|
||||
export const NEXT_CACHE_TAG_MAX_LENGTH = 256
|
||||
export const NEXT_CACHE_SOFT_TAG_MAX_LENGTH = 1024
|
||||
export const NEXT_CACHE_IMPLICIT_TAG_ID = '_N_T_'
|
||||
|
||||
// in seconds
|
||||
|
|
|
@ -3,11 +3,45 @@ import type * as ServerHooks from '../../client/components/hooks-server-context'
|
|||
|
||||
import { AppRenderSpan, NextNodeServerSpan } from './trace/constants'
|
||||
import { getTracer, SpanKind } from './trace/tracer'
|
||||
import { CACHE_ONE_YEAR, NEXT_CACHE_IMPLICIT_TAG_ID } from '../../lib/constants'
|
||||
import {
|
||||
CACHE_ONE_YEAR,
|
||||
NEXT_CACHE_IMPLICIT_TAG_ID,
|
||||
NEXT_CACHE_TAG_MAX_LENGTH,
|
||||
} from '../../lib/constants'
|
||||
import * as Log from '../../build/output/log'
|
||||
|
||||
const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
|
||||
|
||||
export function validateTags(tags: any[], description: string) {
|
||||
const validTags: string[] = []
|
||||
const invalidTags: Array<{
|
||||
tag: any
|
||||
reason: string
|
||||
}> = []
|
||||
|
||||
for (const tag of tags) {
|
||||
if (typeof tag !== 'string') {
|
||||
invalidTags.push({ tag, reason: 'invalid type, must be a string' })
|
||||
} else if (tag.length > NEXT_CACHE_TAG_MAX_LENGTH) {
|
||||
invalidTags.push({
|
||||
tag,
|
||||
reason: `exceeded max length of ${NEXT_CACHE_TAG_MAX_LENGTH}`,
|
||||
})
|
||||
} else {
|
||||
validTags.push(tag)
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidTags.length > 0) {
|
||||
console.warn(`Warning: invalid tags passed to ${description}: `)
|
||||
|
||||
for (const { tag, reason } of invalidTags) {
|
||||
console.log(`tag: "${tag}" ${reason}`)
|
||||
}
|
||||
}
|
||||
return validTags
|
||||
}
|
||||
|
||||
const getDerivedTags = (pathname: string): string[] => {
|
||||
const derivedTags: string[] = [`/layout`]
|
||||
|
||||
|
@ -194,7 +228,10 @@ export function patchFetch({
|
|||
// RequestInit doesn't keep extra fields e.g. next so it's
|
||||
// only available if init is used separate
|
||||
let curRevalidate = getNextField('revalidate')
|
||||
const tags: string[] = getNextField('tags') || []
|
||||
const tags: string[] = validateTags(
|
||||
getNextField('tags') || [],
|
||||
`fetch ${input.toString()}`
|
||||
)
|
||||
|
||||
if (Array.isArray(tags)) {
|
||||
if (!staticGenerationStore.tags) {
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
import { revalidateTag } from './revalidate-tag'
|
||||
import { NEXT_CACHE_IMPLICIT_TAG_ID } from '../../../lib/constants'
|
||||
import { isDynamicRoute } from '../../../shared/lib/router/utils'
|
||||
import {
|
||||
NEXT_CACHE_IMPLICIT_TAG_ID,
|
||||
NEXT_CACHE_SOFT_TAG_MAX_LENGTH,
|
||||
} from '../../../lib/constants'
|
||||
|
||||
export function revalidatePath(path: string, type?: 'layout' | 'page') {
|
||||
path = `${NEXT_CACHE_IMPLICIT_TAG_ID}${path}`
|
||||
export function revalidatePath(originalPath: string, type?: 'layout' | 'page') {
|
||||
if (originalPath.length > NEXT_CACHE_SOFT_TAG_MAX_LENGTH) {
|
||||
console.warn(
|
||||
`Warning: revalidatePath received "${originalPath}" which exceeded max length of ${NEXT_CACHE_SOFT_TAG_MAX_LENGTH}. See more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
let normalizedPath = `${NEXT_CACHE_IMPLICIT_TAG_ID}${originalPath}`
|
||||
|
||||
if (type) {
|
||||
path += `${path.endsWith('/') ? '' : '/'}${type}`
|
||||
normalizedPath += `${normalizedPath.endsWith('/') ? '' : '/'}${type}`
|
||||
} else if (isDynamicRoute(originalPath)) {
|
||||
console.warn(
|
||||
`Warning: a dynamic page path "${originalPath}" was passed to "revalidatePath" without the "page" argument. This has no affect by default, see more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath`
|
||||
)
|
||||
}
|
||||
return revalidateTag(path)
|
||||
return revalidateTag(normalizedPath)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
StaticGenerationAsyncStorage,
|
||||
} from '../../../client/components/static-generation-async-storage.external'
|
||||
import { CACHE_ONE_YEAR } from '../../../lib/constants'
|
||||
import { addImplicitTags } from '../../lib/patch-fetch'
|
||||
import { addImplicitTags, validateTags } from '../../lib/patch-fetch'
|
||||
|
||||
type Callback = (...args: any[]) => Promise<any>
|
||||
|
||||
|
@ -56,7 +56,10 @@ export function unstable_cache<T extends Callback>(
|
|||
isStaticGeneration: !!store?.isStaticGeneration,
|
||||
},
|
||||
async () => {
|
||||
const tags = options.tags || []
|
||||
const tags = validateTags(
|
||||
options.tags || [],
|
||||
`unstable_cache ${cb.toString()}`
|
||||
)
|
||||
|
||||
if (Array.isArray(tags) && store) {
|
||||
if (!store.tags) {
|
||||
|
|
Loading…
Reference in a new issue