From 1835db4a875436f30e8a67dc069f4588562129f4 Mon Sep 17 00:00:00 2001 From: jaywang Date: Sun, 2 Jun 2024 20:03:44 +0800 Subject: [PATCH] vault backup: 2024-06-02 20:03:44 Affected files: content/.obsidian/plugins/recent-files-obsidian/data.json content/.obsidian/workspace.json content/mocs/Data Cache.md content/mocs/React MOC.md content/mocs/Request Memoization.md --- .../plugins/recent-files-obsidian/data.json | 16 ++-- content/.obsidian/workspace.json | 35 +++----- content/mocs/Data Cache.md | 84 +++++++++++++++++++ content/mocs/React MOC.md | 5 +- content/mocs/Request Memoization.md | 53 +++++++++++- 5 files changed, 157 insertions(+), 36 deletions(-) create mode 100644 content/mocs/Data Cache.md diff --git a/content/.obsidian/plugins/recent-files-obsidian/data.json b/content/.obsidian/plugins/recent-files-obsidian/data.json index 8b75627e..7c0a8e59 100644 --- a/content/.obsidian/plugins/recent-files-obsidian/data.json +++ b/content/.obsidian/plugins/recent-files-obsidian/data.json @@ -1,5 +1,9 @@ { "recentFiles": [ + { + "basename": "Data Cache", + "path": "mocs/Data Cache.md" + }, { "basename": "Request Memoization", "path": "mocs/Request Memoization.md" @@ -8,14 +12,14 @@ "basename": "React MOC", "path": "mocs/React MOC.md" }, - { - "basename": "Server Action", - "path": "react/server-action/Server Action.md" - }, { "basename": "useActionState (useFormState)", "path": "react/server-action/useActionState (useFormState).md" }, + { + "basename": "Server Action", + "path": "react/server-action/Server Action.md" + }, { "basename": "useFormStatus", "path": "react/server-action/useFormStatus.md" @@ -195,10 +199,6 @@ { "basename": "Prepositions", "path": "english-grammar/Prepositions.md" - }, - { - "basename": "Prepositions Common Mistakes", - "path": "english-grammar/Prepositions Common Mistakes.md" } ], "omittedPaths": [], diff --git a/content/.obsidian/workspace.json b/content/.obsidian/workspace.json index f9125d62..0f47bef9 100644 --- a/content/.obsidian/workspace.json +++ b/content/.obsidian/workspace.json @@ -13,7 +13,7 @@ "state": { "type": "markdown", "state": { - "file": "mocs/React MOC.md", + "file": "mocs/Data Cache.md", "mode": "source", "source": false } @@ -30,21 +30,8 @@ "source": false } } - }, - { - "id": "64279b3e331b18e7", - "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "react/server-action/useActionState (useFormState).md", - "mode": "source", - "source": false - } - } } - ], - "currentTab": 1 + ] } ], "direction": "vertical" @@ -127,7 +114,7 @@ "state": { "type": "localgraph", "state": { - "file": "mocs/Request Memoization.md", + "file": "mocs/Data Cache.md", "options": { "collapse-filter": true, "search": "", @@ -177,7 +164,7 @@ "state": { "type": "outline", "state": { - "file": "mocs/Request Memoization.md" + "file": "mocs/Data Cache.md" } } }, @@ -218,7 +205,7 @@ "state": { "type": "file-properties", "state": { - "file": "mocs/Request Memoization.md" + "file": "mocs/Data Cache.md" } } }, @@ -228,7 +215,7 @@ "state": { "type": "backlink", "state": { - "file": "mocs/Request Memoization.md", + "file": "mocs/Data Cache.md", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -245,7 +232,7 @@ "state": { "type": "outgoing-link", "state": { - "file": "mocs/Request Memoization.md", + "file": "mocs/Data Cache.md", "linksCollapsed": false, "unlinkedCollapsed": false } @@ -271,12 +258,13 @@ "cmdr:Obsidian Git: Create backup": false } }, - "active": "ffb49d5cb96554c3", + "active": "10a82c220b604f75", "lastOpenFiles": [ - "mocs/React MOC.md", + "mocs/Data Cache.md", "mocs/Request Memoization.md", - "react/server-action/Server Action.md", + "mocs/React MOC.md", "react/server-action/useActionState (useFormState).md", + "react/server-action/Server Action.md", "react/server-action/useFormStatus.md", "web-dev", "browser-event/Event Delegation (event.target).md", @@ -305,7 +293,6 @@ "resume/Avoid Cliches and Buzzwords on resume.md", "english-grammar/English Grammar.md", "mocs/CSS MOC.md", - "mocs/Backend MOC.md", "postgresql", "prisma", "fish-shell", diff --git a/content/mocs/Data Cache.md b/content/mocs/Data Cache.md new file mode 100644 index 00000000..bc313b53 --- /dev/null +++ b/content/mocs/Data Cache.md @@ -0,0 +1,84 @@ +--- +draft: false +date: 2024-06-02 19:33 +tags: + - react + - nextjs + - cache +--- + +By default, Next.js automatically caches data in the Data Cache for every `fetch` request in server components. The Data Cache is the last cache Next.js hits before fetching data from APIs or the database. It is also persistent across multiple requests and users. + +If there are 100 users requesting the same data, Next.js will fetch it only once, store it in the Data Cache, and return it from the cache to 100 users. + +```tsx {3} +export default async function Page({ params }) { + const city = params.city + const res = await fetch(`https://api.globetrotter.com/guides/${city}`) + const guideData = await res.json() + + return ( +
+

{guideData.title}

+

{guideData.content}

+ {/* Render the guide data */} +
+ ) +} +``` + +For example, the `guideData` will only be fetched from the API once and stored in the data cache. All users will get the data from the data cache instead of fetching it from the API. +### Revalidation + +This cache is never cleared, even if you redeploy your application. The only way to update the cache is to explicitly tell Next.js to do so using **time-based revalidation** or **on-demand revalidation.** + +#### Time-based revalidation + +We can tell Next.js to automatically revalidate the data in the data cache by declaring a time period. You can either declare the time as the second parameter (options) in the `fetch` function, or declare it as a config option of the page. + +Next.js implements time-based revalidation with a pattern called `stale-while-revalidate`. Here is how it works: + +1. `fetch` the data from the API and store it in the data cache. +2. Within 1 hour, each `fetch` returns only the data from the data cache. +3. After 1 hour, the first `fetch` returns the data from the data cache, but it also makes the request with API and updates the data cache with new data. +4. The next `fetch` returns the new data in the data cache. + +```tsx title="Revalidate a fetch request" +// The cache of this fetch will be revalidated after 1 hour +const res = fetch(`https://api.globetrotter.com/guides/${city}`, { + next: { revalidate: 3600 }, +}) +``` + +```tsx title="Revalidate whole page" +// all cache of this page will be revalidated after 1 hour +export const revalidate = 3600 + +export default async function Page({ params }) { + const city = params.city + const res = await fetch(`https://api.globetrotter.com/guides/${city}`) + const guideData = await res.json() + + return ( +
+

{guideData.title}

+

{guideData.content}

+ {/* Render the guide data */} +
+ ) +} +``` + +> [!important] +> The time period declared in the fetch function has higher priority than the one declared in the page when two methods are used at the same time. + +#### On-demand Revalidation + + + + + +> [!info] References +> - [Finally Master Next.js's Most Complex Feature - Caching (webdevsimplified.com)](https://blog.webdevsimplified.com/2024-01/next-js-app-router-cache/) +> - [Building Your Application: Caching | Next.js (nextjs.org)](https://nextjs.org/docs/app/building-your-application/caching) +> - [Functions: unstable_cache | Next.js (nextjs.org)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) diff --git a/content/mocs/React MOC.md b/content/mocs/React MOC.md index d2e54d8d..7a2a38af 100644 --- a/content/mocs/React MOC.md +++ b/content/mocs/React MOC.md @@ -1,16 +1,15 @@ --- draft: false -date: 2024-06-02 15:24 +date: 2024-06-02 17:53 tags: - react --- ## Component Design - [[Controlled Components]] - - ## Cache - [[Request Memoization]] +- [[Data Cache]] ## Server Actions diff --git a/content/mocs/Request Memoization.md b/content/mocs/Request Memoization.md index a66add62..e5e7ab93 100644 --- a/content/mocs/Request Memoization.md +++ b/content/mocs/Request Memoization.md @@ -1,16 +1,67 @@ --- draft: false -date: 2024-06-02 15:24 +date: 2024-06-02 17:53 tags: - react - nextjs - cache --- +Request memoization is more of a React feature rather than a Next.js feature. It caches `fetch` results that are made in server components **in a single render cycle**. It will return results from the cache if there are second and more `fetch` requests with same parameters (URL and options). +```tsx {8,17} +export default async function fetchUserData(userId) { + // The `fetch` function is automatically cached by Next.js + const res = await fetch(`https://api.example.com/users/${userId}`) + return res.json(); +} +export default async function Page({ params }) { + const user = await fetchUserData(params.id) + return <> +

{user.name}

+ + +} +async function UserDetails({ id }) { + const user = await fetchUserData(id) + return

{user.name}

+} +``` + +For example, we have `Page` and `UserDetails` server components. When the `user` data is retrieved by the first `fetch` request in the `Page` component, the `user` data is also stored in the request memoization cache. + +After that, the second `fetch` request in the `UserDetails` component will retrieve the stored data from the request memoization cache. The entire process happens in a single render cycle. + +### Caching Non-`fetch` Requests + +By default, React caches all `fetch` requests in request memoization. However, it also provides a `cache` function to wrap other non-`fetch` requests and make them behave like `fetch` requests. + +```tsx +import { cache } from "react" +import { queryDatabase } from "./databaseClient" + +export const fetchUserData = cache(userId => { + // Direct database query + return queryDatabase("SELECT * FROM users WHERE id = ?", [userId]) +}) +``` + +### Opting Out + +If you don't want the request memoization to happen, you can pass a `signal` from the `AbortController` as a parameter to the `fetch` request. This prevents `fetch` result from being cached in the request memoization cache. However, it is not recommended to do this. + +```tsx {2,4} +async function fetchUserData(userId) { + const { signal } = new AbortController() + const res = await fetch(`https://api.example.com/users/${userId}`, { + signal, + }) + return res.json() +} +``` > [!info] References > - [Finally Master Next.js's Most Complex Feature - Caching (webdevsimplified.com)](https://blog.webdevsimplified.com/2024-01/next-js-app-router-cache/)