دریافت داده بخش اصلی هر اپلیکیشنی به شمار می‌آید. در این مقاله قصد داریم تا نحوه fetch، cache و revalidate کردن داده در React و Next.js را باهم بررسی کنیم.

چهار روش در Next.js برای دریافت کردن داده وجود دارد که عبارتند از:

دریافت داده بر روی سرور با استفاده از fetch در Next.js

کاری که Next.js انجام می‌دهد این است که fetch Web API native را extend می‌کند تا به ما اجازه دهد رفتار caching و revalidating را برای هر درخواست fetch بر روی سرور پیکربندی کنیم. کاری که React انجام می‌دهد این است که

fetch
fetchرا extend می‌کند تا حین رندر کردن درخت کامپوننت React، درخواست‌های fetch به طور خودکار به خاطر سپرده شوند.

ما می‌توانیم از

fetch
fetch همراه با
async
async/
await
awaitدر سرور کامپوننت‌ها، در Route Handlerها و همینطور در Server Actionها استفاده نماییم. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/...')
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
app/page.tsx async function getData() { const res = await fetch('https://api.example.com/...') // The return value is *not* serialized // You can return Date, Map, Set, etc. if (!res.ok) { // This will activate the closest `error.js` Error Boundary throw new Error('Failed to fetch data') } return res.json() } export default async function Page() { const data = await getData() return <main></main> }
app/page.tsx

async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
 
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }
 
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
 
  return <main></main>
}

بهتر است به این نکات توجه داشته باشیم که:

Caching داده‌ها

Caching داده‌ها را ذخیره می‌کند، بنابراین نیازی نیست در هر درخواستی برای دریافت مجدد داده‌ها، آن‌ها را از منبع داده‌ای که داریم fetch کنیم.

به‌طور پیش‌فرض، Next.js به صورت خودکار مقادیر بازگشتی

fetch
fetch را در حافظه cache بر روی سرور ذخیره می‌کند. این بدان معنی است که ما می‌توانیم داده‌ها را در زمان ساخت یا زمان درخواست fetch کنیم، cache کنیم و در هر درخواست داده دوباره از آن استفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 'force-cache' is the default, and can be omitted
fetch('https://...', { cache: 'force-cache' })
// 'force-cache' is the default, and can be omitted fetch('https://...', { cache: 'force-cache' })
// 'force-cache' is the default, and can be omitted
fetch('https://...', { cache: 'force-cache' })

درخواست‌های

fetch
fetchکه از متد
POST
POSTاستفاده می‌کنند هم به‌طور خودکار ذخیره می‌شوند. مگر اینکه داخل یک Route Handler باشد که از متد
POST
POST استفاده می‌کند، در این صورت ذخیره نمی‌شود.

Revalidating داده‌ها

Revalidation در Next.js فرآیند پاکسازی حافظه cache و دریافت کردن مجدد آخرین داده ها می‌باشد. این موضوع زمانی مفید است که داده‌ها تغییر می‌کند و ما می‌خواهیم مطمئن شویم که آخرین اطلاعات را به کاربران نمایش می‌دهیم.

داده‌های ذخیره شده را می‌توانیم به دو روش revalidate کنیم:

Revalidation مبتنی بر زمان

برای revalidate داده‌ها در یک بازه زمانی معین، می‌توانیم از گزینه

next.revalidate
next.revalidateمربوط به
fetch
fetchبرای تنظیم طول عمر حافظه cache یک منبع (در ثانیه) استفاده کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
fetch('https://...', { next: { revalidate: 3600 } })
fetch('https://...', { next: { revalidate: 3600 } })
fetch('https://...', { next: { revalidate: 3600 } })

از طرف دیگر، برای revalidate همه درخواست‌های

fetch
fetch در یک segment مسیر، می‌توانیم از گزینه‌های پیکربندی Segment استفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
layout.js | page.js
export const revalidate = 3600 // revalidate at most every hour
layout.js | page.js export const revalidate = 3600 // revalidate at most every hour
layout.js | page.js

export const revalidate = 3600 // revalidate at most every hour

اگر چندین درخواست fetch در یک مسیر رندر شده استاتیک داشته باشیم و هر کدام فرکانس revalidation متفاوتی داشته باشند؛ در این صورت کم‌ترین زمان برای همه درخواست‌ها مورد استفاده قرار می‌گیرد. اما برای مسیرهای رندر شده به صورت پویا، هر درخواست

fetch
fetchمستقلاً revalidate می‌شود.

Revalidation مبتنی بر تقاضا

می‌توانیم داده‌ها را بر حسب تقاضا توسط path (

revalidatePath
revalidatePath) یا توسط tag حافظه cache (
revalidateTag
revalidateTag) در داخل یک Server Action یا Route Handler، مجددا revalidated کنیم.

Next.js یک سیستم cache tagging برای باطل کردن درخواست‌های

fetch
fetchدر مسیرها دارد.

  1. هنگام استفاده از
    fetch
    fetch، این گزینه را داریم که ورودی‌های cache را با یک یا چند تگ برچسب‌گذاری کنیم.
  2. سپس، می‌توانیم
    revalidateTag
    revalidateTagرا فراخوانی کنیم تا از این طریق همه ورودی‌های مرتبط با آن تگ را revalidate نماییم.

به عنوان مثال، درخواست fetch زیر تگ cache

collectio
collectio را اضافه می‌کند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/page.tsx
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
app/page.tsx export default async function Page() { const res = await fetch('https://...', { next: { tags: ['collection'] } }) const data = await res.json() // ... }
app/page.tsx

export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

سپس می‌توانیم این

fetch
fetchبرچسب‌گذاری شده با
collection
collectionرا با فراخوانی
revalidateTag
revalidateTagدر یک Server Action مجدداً revalidate کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
app/actions.ts 'use server' import { revalidateTag } from 'next/cache' export default async function action() { revalidateTag('collection') }
app/actions.ts

'use server'
 
import { revalidateTag } from 'next/cache'
 
export default async function action() {
  revalidateTag('collection')
}

مدیریت خطا و  revalidation

اگر هنگام تلاش برای revalidate کردن داده‌ها خطایی رخ بدهد، در این صورت آخرین داده‌هایی که با موفقیت تولید شده بودند همچنان از حافظه cache ارائه می‌شوند. سپس درخواست بعدی، Next.js مجدداً داده‌ها را revalidate می‌کند.

انصراف از Data Caching

درخواست‌های

fetch
fetchدر حافظه cache ذخیره نمی‌شوند اگر:

درخواست‌های fetch منفرد

برای انصراف از ذخیره‌سازی برای درخواست‌های

fetch
fetchمنفرد، می‌توانیم گزینه
cache
cacheرا در
fetch
fetch بر روی
'no-store'
'no-store'تنظیم کنیم. به این ترتیب داده‌ها به صورت پویا و در هر درخواست، دریافت می‌شوند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
layout.js | page.js
fetch('https://...', { cache: 'no-store' })
layout.js | page.js fetch('https://...', { cache: 'no-store' })
layout.js | page.js

fetch('https://...', { cache: 'no-store' })

درخواست‌های fetch چندگانه

اگر چندین درخواست

fetch
fetch در یک segment مسیر داشته باشیم (مثلاً یک Layout یا Page)، می‌توانیم رفتار ذخیره‌سازی تمام درخواست‌های داده در segment را با استفاده از گزینه‌های پیکربندی Segment پیکربندی کنیم.

با این حال، توصیه می‌شود که رفتار ذخیره‌سازی برای هر درخواست

fetch
fetchرا به‌صورت جداگانه پیکربندی کنیم. این کار کمک می‌کند تا کنترل دقیق‌تری بر رفتار caching داشته باشیم.

دریافت داده بر روی سرور با استفاده از کتابخانه‌های third-party در Next.js

در مواردی که از کتابخانه third-party استفاده می‌کنیم که

fetch
fetchرا پشتیبانی نمی‌کند یا در معرض نمایش قرار نمی‌دهد (به عنوان مثال، یک پایگاه داده، CMS یا ORM کلاینت)، می‌توانیم رفتار caching و revalidating آن درخواست‌ها را با استفاده از گزینه پیکربندی Segment مسیر و تابع
cache
cacheReact پیکربندی نماییم.

اینکه داده‌ها در حافظه cache ذخیره می‌شوند یا نه به این موضوع بستگی دارد که Segment مسیر به صورت استاتیک رندر شده است یا به صورت پویا. اگر Segment استاتیک (پیش‌فرض) باشد، خروجی درخواست به‌عنوان بخشی از Segment مسیر در حافظه cache ذخیره شده و revalidate می‌شود. اگر Segment پویا باشد، خروجی درخواست در حافظه cache ذخیره نمی‌شود و در هر درخواستی که Segment رندر می‌شود، دوباره fetch می‌گردد.

همچنین می‌توانیم از API آزمایشی

unstable_cache
unstable_cacheاستفاده کنیم.

به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/utils.ts
import { cache } from 'react'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
app/utils.ts import { cache } from 'react' export const getItem = cache(async (id: string) => { const item = await db.item.findUnique({ id }) return item })
app/utils.ts

import { cache } from 'react'
 
export const getItem = cache(async (id: string) => {
  const item = await db.item.findUnique({ id })
  return item
})

اگرچه تابع

getItem
getItemدو بار فراخوانی می‌شود، اما تنها یک query در پایگاه داده انجام می‌گردد.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/item/[id]/layout.tsx
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // revalidate the data at most every hour
export default async function Layout({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
app/item/[id]/layout.tsx import { getItem } from '@/utils/get-item' export const revalidate = 3600 // revalidate the data at most every hour export default async function Layout({ params: { id }, }: { params: { id: string } }) { const item = await getItem(id) // ... }
app/item/[id]/layout.tsx

import { getItem } from '@/utils/get-item'
 
export const revalidate = 3600 // revalidate the data at most every hour
 
export default async function Layout({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/item/[id]/page.tsx
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // revalidate the data at most every hour
export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
app/item/[id]/page.tsx import { getItem } from '@/utils/get-item' export const revalidate = 3600 // revalidate the data at most every hour export default async function Page({ params: { id }, }: { params: { id: string } }) { const item = await getItem(id) // ... }
app/item/[id]/page.tsx

import { getItem } from '@/utils/get-item'
 
export const revalidate = 3600 // revalidate the data at most every hour
 
export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}

دریافت داده بر روی کلاینت با استفاده از Route Handlerها در Next.js

اگر نیاز به fetch کردن داده‌ها در یک کلاینت کامپوننت داریم، می‌توانیم یک Route Handler را از کلاینت فراخوانی کنیم. Route Handlerها روی سرور اجرا می‌شوند و داده‌ها را به کلاینت retrun می‌کنند. این کار زمانی مفید است که نمی‌خواهیم اطلاعات حساسی مانند توکن‌های API را در اختیار کلاینت قرار دهیم.

نکته‌ای که باید درمورد سرور کامپوننت‌ها و Route Handlerها به آن توجه کنیم این است که سرور کامپوننت‌ها روی سرور رندر می‌شوند، از این رو برای دریافت کردن داده‌ها نیازی به فراخوانی Route Handler از یک سرور کامپوننت نداریم. در عوض، می‌توانیم داده‌ها را مستقیماً در داخل سرور کاپموننت دریافت نماییم.

دریافت داده بر روی کلاینت با استفاده از کتابخانه‌های third-party در Next.js

همچنین می‌توانیم با استفاده از یک کتابخانه third-party مانند SWR یا TanStack Query، داده‌ها را روی کلاینت دریافت کنیم. این کتابخانه‌ها برای به خاطر سپردن درخواست‌ها، caching، revalidating و mutating داده‌ها APIهای خود را ارائه می‌کنند.

درمورد APIهای آینده باید به این موضوع توجه کنیم که use یک تابع React است که promiseای را که توسط یک تابع return می‌شود را می‌پذیرد وآن را مدیریت می‌کند. در حال حاضر قرار دادن fetch در تابع

use
use در کلاینت کامپوننت‌ها توصیه نمی‌شود و ممکن است باعث ایجاد رندرهای مجدد شود.