You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -39,7 +39,7 @@ For each of the above, I will delve into their specific roles, where they're sto
39
39
40
40
### Request Memoization
41
41
42
-
If you have ever built an application before, you have probably had to fetch and display the same information in multiple places. As an example, if you are fetching some user data, it might need to be displayed in more than one place. There is one problem, though. If our `fetch` request is computationally heavy, this can increase the load on the server if it's continuously fetching from a data source. This becomes even more problematic when we consider the fact that user details are unlikely to change within a single request. So, it doesn’t really make sense to make a fresh `fetch` call each time we want the user details.
42
+
If you have ever built an application before, you have probably had to fetch and display the same information in multiple places. As an example, if you are fetching some user data, it might need to be displayed in more than one place. There is one problem, though. If our `fetch` request is computationally heavy, this can increase the load on the server if it's continuously fetching from a data source. This becomes even more problematic when we consider the fact that user details are unlikely to change within a single request. So, it doesn't really make sense to make a fresh `fetch` call each time we want the user details.
43
43
44
44
Luckily, Request Memoization solves this exact problem. Whenever we make a `fetch` request in a render cycle (which basically just refers to the process of rendering all the components on a page), its result gets stored for any subsequent requests that have identical parameters.
45
45
@@ -75,37 +75,37 @@ While Request Memoization in Next.js is tailored only for the `GET` method in th
75
75
To do this, we can uset React's `cache()` function.
76
76
77
77
```jsx
78
-
import { cache } from"react";
79
-
import { queryDatabase } from"./databaseClient";// Assume this is our database client function
78
+
import { cache } from"react"
79
+
import { queryDatabase } from"./databaseClient"// Assume this is our database client function
80
80
81
81
exportdefaultasyncfunctionfetchUserData(userId) {
82
82
returncache(async () => {
83
83
// Direct database query
84
-
returnqueryDatabase("SELECT * FROM users WHERE id = ?", [userId]);
85
-
});
84
+
returnqueryDatabase("SELECT * FROM users WHERE id = ?", [userId])
85
+
})
86
86
}
87
87
```
88
88
89
-
In this code above, the first time `fetchUserData()` is called, it queries the database directly, as there’s no cached result yet. But the next time this function is called with the same `userId` in a different component, it smartly retrieves from the cache. Just like with `fetch`, this memoization is a one-time deal, valid only for the duration of a single render pass, but at least now we know that we aren’t limited to `fetch`.
89
+
In this code above, the first time `fetchUserData()` is called, it queries the database directly, as there's no cached result yet. But the next time this function is called with the same `userId` in a different component, it smartly retrieves from the cache. Just like with `fetch`, this memoization is a one-time deal, valid only for the duration of a single render pass, but at least now we know that we aren't limited to `fetch`.
90
90
91
91
#### Revalidation
92
92
93
93
In Next.js, revalidation refers to the process of refreshing old cached data with new data straight from the database. This feature is vital for determining how long the data you serve stays up-to-date in the cache, ensuring that your users always have access to the most relevant information.
94
94
95
-
As of right now, there is no way we can revalidate the memoized values. This might seem like a limitation, but memoization is generally for a single render pass, and it’s unlikely that your data will change in that short interval. Still, if you find yourself needing to revalidate, there is an option to opt out.
95
+
As of right now, there is no way we can revalidate the memoized values. This might seem like a limitation, but memoization is generally for a single render pass, and it's unlikely that your data will change in that short interval. Still, if you find yourself needing to revalidate, there is an option to opt out.
96
96
97
97
#### Opting out
98
98
99
99
To opt out of the cache, we can pass in an `AbortController` signal as a parameter to the `fetch` request.
100
100
101
101
```tsx {1, 4, 5}
102
-
const { signal } =newAbortController();
102
+
const { signal } =newAbortController()
103
103
asyncfunction fetchUserData(userId) {
104
104
// The `fetch` function is automatically memoized by Next.js during SSR
105
105
const res =awaitfetch(`https://api.example.com/users/${userId}`, {
106
106
signal,
107
-
});
108
-
returnres.json();
107
+
})
108
+
returnres.json()
109
109
}
110
110
```
111
111
@@ -128,21 +128,21 @@ To do this, we can use the Data Cache, which is enabled by default in Next.js. T
In the simple code I have above, all we are doing is making a `fetch` request to an API and dynamically passing in a `city` parameter so we can display travel guides for a given city. The main thing to focus on here is the `fetch` request. If there are four users accessing this page with the same city parameter, Next.js will `fetch` the data after the first user's request and store it in the Data Cache. This means that for the next three users, no actual fetch call is made as it’s retrieved directly from the Data Cache. In other words, the Data Cache allows us to reduce calls to our original data source, providing a much smoother experience for our users.
145
+
In the simple code I have above, all we are doing is making a `fetch` request to an API and dynamically passing in a `city` parameter so we can display travel guides for a given city. The main thing to focus on here is the `fetch` request. If there are four users accessing this page with the same city parameter, Next.js will `fetch` the data after the first user's request and store it in the Data Cache. This means that for the next three users, no actual fetch call is made as it's retrieved directly from the Data Cache. In other words, the Data Cache allows us to reduce calls to our original data source, providing a much smoother experience for our users.
146
146
147
147
#### `unstable_cache`
148
148
@@ -151,23 +151,23 @@ So far, we have only seen how to cache `fetch` requests with the Data Cache, but
151
151
If we go back to our previous example of city guides, we might want to pull data directly from our database. For this, we can use the `unstable_cache()` function that's provided by Next.js. This is like Next.js's version of the `cache()`, except it applies to the Data Cache.
152
152
153
153
```jsx {4}
154
-
import { getGuides } from"./data";
155
-
import { unstable_cache } from"next/cache";
154
+
import { getGuides } from"./data"
155
+
import { unstable_cache } from"next/cache"
156
156
157
157
constgetCachedGuides=unstable_cache(
158
-
async(city)=>getGuides(city),
158
+
asynccity=>getGuides(city),
159
159
["guides-cache-key"]
160
-
);
160
+
)
161
161
162
162
exportdefaultasyncfunctionPage({ params }) {
163
-
constguides=awaitgetCachedGuides(params.city);
163
+
constguides=awaitgetCachedGuides(params.city)
164
164
// ...
165
165
}
166
166
```
167
167
168
168
The code above is short, but it can be confusing if this is the first time you are seeing the `unstable_cache()` function being used, so let me explain.
169
169
170
-
I am using the `getGuides()` function to fetch guides from the database given a city (I left out its implementation as it’s not important). Then, I am declaring a `const` called `getCachedGuides` and using it to store the result I get from calling the `unstable_cache()` function. You will notice there are two parameters: the `getGuides()` function and an array. This array `['guides-cache-key']` acts like a unique identifier and assigns our cached guides a category.
170
+
I am using the `getGuides()` function to fetch guides from the database given a city (I left out its implementation as it's not important). Then, I am declaring a `const` called `getCachedGuides` and using it to store the result I get from calling the `unstable_cache()` function. You will notice there are two parameters: the `getGuides()` function and an array. This array `['guides-cache-key']` acts like a unique identifier and assigns our cached guides a category.
171
171
172
172
Then, when we call `getCachedGuides()` inside Page, it will act exactly the same as the fetch requests did previously with the Data Cache.
173
173
@@ -184,7 +184,7 @@ One approach is called time-based revalidation. This is pretty easy to grasp. Es
With the code above, our application will `fetch` the most updated data from the data source every hour. One potential problem with this approach for our use case is that if an article is added within the revalidation period, users will only see the update after it's revalidated. Also, if no new content is added for hours on end, our code will still refresh the Data Cache.
@@ -196,7 +196,7 @@ When we want to invalidate the cache and fetch new data only when a new article
196
196
On-demand revalidation shines when you want to refresh data in response to specific events.
197
197
198
198
```jsx {10}
199
-
import { revalidatePath } from"next/cache";
199
+
import { revalidatePath } from"next/cache"
200
200
201
201
exportasyncfunctionpublishArticle() {
202
202
try {
@@ -205,7 +205,7 @@ export async function publishArticle() {
205
205
// ...
206
206
}
207
207
208
-
revalidatePath("/guides/paris");
208
+
revalidatePath("/guides/paris")
209
209
}
210
210
```
211
211
@@ -221,13 +221,13 @@ We can control revalidation for specific tags.
In the example above, we've tweaked our `fetch` by adding `cache:"no-store"`. This opts a specific `fetch` request out of the Data Cache.
@@ -262,7 +262,7 @@ In the example above, we've tweaked our `fetch` by adding `cache:"no-store"`. Th
262
262
If we want to change the caching behavior for an entire page and not just a specific `fetch` request, we can add this piece of code to the top level of our file.
263
263
264
264
```jsx
265
-
exportconstdynamic="force-dynamic";
265
+
exportconstdynamic="force-dynamic"
266
266
```
267
267
268
268
##### `export const revalidate = 0`
@@ -281,28 +281,28 @@ The third type of cache I will cover is the Full Route Cache, which is also enab
281
281
282
282
In Next.js, the pages we render to our clients consist of HTML and something called the React Server Component Payload (RSCP). The payload contains instructions for how the client components should work together with the rendered server components to render the page. The Full Route Cache stores the HTML and RSCP for static pages at Build Time.
283
283
284
-
Now that we know what it stores, let’s take a look at an example.
284
+
Now that we know what it stores, let's take a look at an example.
@@ -312,7 +312,7 @@ export default async function Page() {
312
312
))}
313
313
</ul>
314
314
</div>
315
-
);
315
+
)
316
316
}
317
317
```
318
318
@@ -351,18 +351,18 @@ The diagram below demonstrates the step-by-step process of how Full Route Cache
351
351
352
352
### Router Cache
353
353
354
-
The last cache I am going to talk about is the router cache. It is the only client-side cache in Next.js and can be the source of many bugs if not understood properly. This is mainly because it is enabled by default and caches routes that have already been visited before. Typically, the browser needs to re-fetch the data and re-render a page when it is visited again. This process, of course, takes longer than caching a page, which is why Next.js takes the caching approach. While this approach is an advantage when it comes to page loading speeds, it can also be quite frustrating. Let’s take a look below at why.
354
+
The last cache I am going to talk about is the router cache. It is the only client-side cache in Next.js and can be the source of many bugs if not understood properly. This is mainly because it is enabled by default and caches routes that have already been visited before. Typically, the browser needs to re-fetch the data and re-render a page when it is visited again. This process, of course, takes longer than caching a page, which is why Next.js takes the caching approach. While this approach is an advantage when it comes to page loading speeds, it can also be quite frustrating. Let's take a look below at why.
355
355
356
356
```jsx {10,11,12}
357
357
// /page.jsx
358
358
exportdefaultasyncfunctionPage() {
359
-
constblogData=awaitgetBlogList();
359
+
constblogData=awaitgetBlogList()
360
360
361
361
return (
362
362
<div>
363
363
<h1>Blog Posts</h1>
364
364
<ul>
365
-
{blogData.map((post)=> (
365
+
{blogData.map(post=> (
366
366
<li key={post.slug}>
367
367
<Link href={`/blog/${post.slug}`}>
368
368
<a>{post.title}</a>
@@ -372,11 +372,11 @@ export default async function Page() {
372
372
))}
373
373
</ul>
374
374
</div>
375
-
);
375
+
)
376
376
}
377
377
```
378
378
379
-
In the code I have above, when the user navigates to `/page`, its RSCP gets stored in the Router Cache. Similarly, when we navigate to `/blog`, its RSCP also gets cached. The problem arises when we navigate back from `/blog` to `/page` within a certain period. This means that if we have anything dynamic on `/page`, it won’t get updated until after the cache is invalidated or we refresh our page. Essentially, what’s happening is that Next.js is retrieving the cached RSCP for `/page` from the Router Cache, sacrificing data freshness for performance. This approach might lead us to see stale data if we were expecting it to update upon navigation.
379
+
In the code I have above, when the user navigates to `/page`, its RSCP gets stored in the Router Cache. Similarly, when we navigate to `/blog`, its RSCP also gets cached. The problem arises when we navigate back from `/blog` to `/page` within a certain period. This means that if we have anything dynamic on `/page`, it won't get updated until after the cache is invalidated or we refresh our page. Essentially, what's happening is that Next.js is retrieving the cached RSCP for `/page` from the Router Cache, sacrificing data freshness for performance. This approach might lead us to see stale data if we were expecting it to update upon navigation.
380
380
381
381
Before we jump into the details of revalidating this cache, it's worth noting a side point: by default, Next.js also analyzes the structure of our web page, paying special attention to `<Link>` components that the user might click on next. It then preemptively fetches the data or resources for those pages to make them load faster upon request.
382
382
@@ -417,14 +417,14 @@ In our example, this is useful when a user comments on one of the blog posts. Af
417
417
418
418
```jsx
419
419
// After submitting a comment
420
-
router.refresh();
420
+
router.refresh()
421
421
```
422
422
423
423
If our site has personalized content that changes based on user login status (like showing recommended articles), logging in or out should update what's displayed.
424
424
425
425
```jsx
426
426
// On login or logout
427
-
cookies.set("authToken", userToken);// or cookies.delete('authToken');
427
+
cookies.set("authToken", userToken) // or cookies.delete('authToken');
428
428
```
429
429
430
430
By using the above, we update the authentication cookies and the Router Cache is automatically invalidated.
0 commit comments