Course
Request Memoization
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Introduction
In this chapter, we will introduce the caching mechanisms in Next.js.
The caching feature in Next.js is incredibly powerful, often resulting in a love-hate relationship. On one hand, caching is crucial for optimizing application performance and reducing costs. On the other hand, when working on a Next.js project, you may frequently encounter issues where data does not update, usually because of caching...
In theory, understanding caching is not essential for using Next.js since it manages caching automatically based on the APIs you use. However, in practice, it's crucial to understand caching to resolve issues effectively when they arise.
Let's dive into caching now.
Overview
Next.js has four types of caching mechanisms:
By default, Next.js uses caching as much as possible to improve performance and reduce costs. For example, routes are statically rendered by default, and the results of data requests are cached. The diagram below shows the process of static route rendering at build time and the first visit to a static route:
In this diagram:
- During the build time for
/a
, the requests are made for the first time, resulting in a cache miss. Data is fetched from the data source, stored in request memoization and data cache (SET), and the resulting RSC Payload and HTML are stored in the server's full route cache. - When the client visits
/a
, the cached RSC Payload and HTML are retrieved from the server's cache, and the RSC Payload is also stored in the client's router cache.
Caching behavior varies depending on several factors, such as whether the route is dynamically or statically rendered, whether the data is cached or uncached, and whether the request is part of the initial access or subsequent navigation.
Feeling confused? Don't worry. As we progress through the content, you'll gain a deeper understanding.
Request Memoization
1. How It Works
React extends the fetch API to automatically cache the results of requests with the same URL and parameters. This means that even if you request the same data in multiple places in the component tree, the data will only be fetched once.
This allows you to request data directly in the component where it's needed without worrying about the performance impact of making the same request multiple times. You don't need to fetch data at the top level and pass it down via props.
// app/page.jsasync function getItem() { // Automatically cache the result const res = await fetch('https://.../item/1'); return res.json();}
// Function called twice, but only one request will be madeconst item = await getItem(); // cache MISS
const item = await getItem(); // cache HIT
This is the principle behind request memoization:
In this diagram, when rendering the
/a
route, the first request triggers a cache miss. The function is executed, and the request result is stored in memory (cache SET). On subsequent identical calls, the cached data is retrieved from memory (cache HIT).The underlying principle is similar to function memoization. Here's an example from "JavaScript: The Definitive Guide":
function memoize(f) { var cache = {}; return function() { var key = arguments.length + Array.prototype.join.call(arguments, ","); if (key in cache) { return cache[key]; } else { return cache[key] = f.apply(this, arguments); } }}
2. Duration
The cache lasts for the duration of the server request lifecycle until the React component tree has finished rendering. It is intended to prevent multiple requests for the same data during the rendering of the component tree, improving performance.
3. Revalidation
Since request memoization is only used during rendering, there is no need for revalidation.
4. Opting Out
This behavior is a default optimization in React and is not recommended to opt-out of.
If you don't want a fetch request to be memoized, you can use the AbortController Web API (although its primary purpose is to abort requests):
const { signal } = new AbortController();fetch(url, { signal });
5. React Cache
If you cannot use fetch but still want to implement memoization, you can use React's cache function:
// utils/get-item.tsimport { cache } from 'react';import db from '@/lib/db';
export const getItem = cache(async (id) => { const item = await db.item.findUnique({ id }); return item;});
Note: To better understand request memoization and data caching, practical examples are provided at the end of this chapter.