Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Address internal use cache comments #71981

Merged
merged 4 commits into from
Oct 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions docs/02-app/02-api-reference/01-directives/use-cache.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@ const nextConfig: NextConfig = {
export default nextConfig
```

> The `use cache` directive will be available separately from the`dynamicIO` flag in the future.
> The `use cache` directive will be available separately from the `dynamicIO` flag in the future.

Caching is a technique to improve the performance of web applications by storing the results of computations or data fetches. In Next.js you can use caching to optimize your applications rendering performance.

To explicitly cache certain asynchronous operations and achieve static behavior, you can use the `use cache` directive. This allows you to optimize rendering performance by caching results from async data requests, while still enabling dynamic rendering when needed.
Caching is a technique used to improve the performance of web applications by storing the results of rendering or data requests. Whenever you use an asynchronous function or APIs that depend on request-time data, Next.js will automatically opt into dynamic rendering. You can explicitly cache the results of these operations and optimize your application's rendering performance with the `use cache` directive.

The `use cache` directive is an experimental feature that aims to replace the [`unstable_cache`](/docs/app/api-reference/legacy-apis/unstable_cache) function. Unlike `unstable_cache`, which is limited to caching JSON data and requires manual definition of revalidation periods and tags, `use cache` offers more flexibility. It allows you to cache a wider range of data, including anything that React Server Components (RSC) can serialize, as well as data-fetching outputs and component outputs.

Expand Down Expand Up @@ -69,9 +67,9 @@ export async function getData() {

## Revalidating

By default when using the `use cache` directive Next.js sets a **[revalidation period](/docs/app/building-your-application/data-fetching/fetching#revalidating-cached-data) of fifteen minutes** with a near infinite expiration duration, meaning it's suitable for content that doesn't need frequent updates.
By default, Next.js sets a **[revalidation period](/docs/app/building-your-application/data-fetching/fetching#revalidating-cached-data) of 15 minutes** when you use the `use cache` directive. Next.js sets a with a near-infinite expiration duration, meaning it's suitable for content that doesn't need frequent updates.

While this may be useful for content you don't expect to change often, you can use the `cacheLife` and `cacheTag` APIs for more granular caching control:
While this revalidation period may be useful for content you don't expect to change often, you can use the `cacheLife` and `cacheTag` APIs to configure the cache behavior:

- [`cacheLife`](#time-based-revalidation-with-cachelife): For time-based revalidation periods.
- [`cacheTag`](#revalidate-on-demand-with-cachetag): For on-demand revalidation.
Expand Down Expand Up @@ -150,7 +148,7 @@ The string values used to reference cache profiles don't carry inherent meaning;

### Defining reusable cache profiles

To create a reusable cache profile, choose a name that suits your use case. You can create as many custom cache profiles as needed. Each profile can be referenced by its name as a string value passed to the `cacheLife` function.
You can create a reusable cache profile by defining them in your `next.config.ts` file. Choose a name that suits your use case and set values for the `stale`, `revalidate`, and `expire` properties. You can create as many custom cache profiles as needed. Each profile can be referenced by its name as a string value passed to the `cacheLife` function.

```ts filename="next.config.ts"
const nextConfig = {
Expand Down Expand Up @@ -205,7 +203,7 @@ const nextConfig = {
module.exports = nextConfig
```

### Defining inlining cache profiles
### Defining cache profiles inline

For specific use cases, you can set a custom cache profile by passing an object to the `cacheLife` function:

Expand All @@ -226,13 +224,13 @@ This inline cache profile will only be applied to the function or file it was cr

### Nested usage of `use cache` and `cacheLife`

When defining multiple caching behaviors, in the same route or component tree, if the inner caches specify their own `cacheLife` profile, the outer cache will respect the shortest cache duration among them. **This applies only if the outer cache does not have its own explicit `cacheLife` profile defined.**
When defining multiple caching behaviors in the same route or component tree, if the inner caches specify their own `cacheLife` profile, the outer cache will respect the shortest cache duration among them. **This applies only if the outer cache does not have its own explicit `cacheLife` profile defined.**

**Decision hierarchy for cache boundaries:**

1. Next.js will use the shortest cache profile found within the whole `use cache` boundary, excluding inner `use cache` directives.
2. If no cache profile exists then the shortest profile times from all inner `use cache` calls applies to this `use cache`. If there are no inner `use cache`'s then the default is used
3. Inner caches at two levels deep, do not affect the outer cache since they have already provided their duration to their parent.
2. If no cache profile exists, then the shortest profile times from all inner `use cache` calls applies to this `use cache`. If there are no inner `use cache`'s then the default is used
3. Inner caches at two levels deep do not affect the outer cache since they have already provided their duration to their parent.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to say I still don't have a clear understanding of the nested behavior. There is an example below but it doesn't actually show what's going to happen. The way I'm reading it now, I assume that ChildComponent will be days-stale in the context of ParentComponent (due to the outer boundary, rule #1) but would be hours-stale if it was rendered on its own. Is that the expectation? This should ideally be spelled out just below the example — the code comment seems to contradict that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constraints I care about as an app author:

  • When I give a cache lifetime to the child, do I have a guarantee it will update at least as often? Even if that forces everything above to invalidate?
  • Or does the outer scope "win" and outer scope can "freeze" inner scopes that would've liked to revalidate more often?
  • When I want to avoid this conflict, how do I break out of it? (I assume via the children composition trick instead of nested scopes?)

It would be good to write this part from this perspective. The concerns that need to be addressed is (1) is my stuff ever going to be more stale than locally expected, (2) is my stuff going to revalidate more often than locally expected, (3) how to resolve conflicts between expectations if there's a clear "hole" that can be cut.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback Dan, I'll address this and other community questions in a follow-up PR. 🙏🏼


For example, if you add the `use cache` directive to your page, without specifying a cache profile, the default cache profile will be applied implicitly (`cacheLife(”default”)`). If a component imported into the page also uses the `use cache` directive with its own cache profile, the outer and inner cache profiles are compared, and shortest duration set in the profiles will be applied.

Expand Down Expand Up @@ -272,9 +270,9 @@ export async function ChildComponent() {

A `cacheTag` is used in combination with [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) to purge cache data, on-demand. The `cacheTag` function takes a single string value, or a string array.

In the below example the `getData` function uses the “weeks” cache profile, and defines a `cacheTag` on the functions cached output:
In the following example,the `getData` function uses the “weeks” cache profile, and defines a `cacheTag` on the functions cached output:

```tsx filename="app/actions.ts" highlight={8,9}
```tsx filename="app/actions.ts" highlight={2,9}
import {
unstable_cacheTag as cacheTag,
unstable_cacheLife as cacheLife,
Expand All @@ -290,9 +288,9 @@ export async function getData() {
}
```

You can then purge the cache on-demand using `revalidateTag` in another function, for examples, a [route handler](/docs/app/building-your-application/routing/route-handlers) or [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations):
You can then purge the cache on-demand using [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) API in another function, for example, a [route handler](/docs/app/building-your-application/routing/route-handlers) or [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations):

```tsx filename="app/submit.ts" highlight={6,7}
```tsx filename="app/submit.ts" highlight={3,7}
'use server'

import { revalidateTag } from 'next/cache'
Expand Down
Loading