IlmHamroh
JavaScript Full-stack/23-qism. Next.js20/43-dars17 daqiqa
Mundarija (33)

React cache() va Data Access Layer: Next.js'da ma'lumot qatlami

Qisqacha: cache() — React funksiyasi: u o'ralgan funksiyaning natijasini bitta server so'rovi (render) davomida eslab qoladi. generateMetadata va sahifa bir xil postni so'rasa, haqiqiy o'qish bir marta bo'ladi. Keyingi so'rovda xotira toza — bu "use cache" emas, u so'rovlararo kesh. Data Access Layer (DAL — ma'lumotga murojaat qatlami) — sahifalar ma'lumotni faqat bitta server modulidan oladigan naqsh. Bu modul server-only bilan himoyalangan, ruxsat tekshiruvi ma'lumot yonida turadi va sahifaga faqat kerakli maydonlar — DTO qaytariladi.

Bu darsda

  • cache() bilan "use cache" farqini aniq aytasiz: bitta so'rov va ko'p so'rov.
  • Kanon o'lchovida cache() chaqiruvlarni 4 dan 2 ga tushirganini va qachon foydasiz ekanini tushuntirasiz.
  • Natija oynasida cache() ning client'dagi xulqini o'zingiz ko'rasiz (u yerda u hech narsani eslamaydi).
  • dal.ts yozasiz: server-only, cache(), DTO — va ruxsat tekshiruvi qayerga qo'yilishini bilasiz.
  • portfolio ga DAL va loyiha/post sahifalariga generateMetadata qo'shib, holat 20 ni commit qilasiz.

Oldin bilishingiz kerak: Serverda ma'lumot olish, Cache Components I, Server va Client kompozitsiyasi, Memoizatsiya, Frontend xavfsizligi React'da.

1. Nega bu kerak?

Blog posti sahifasini oching. Unga ikki joyda post ma'lumoti kerak:

  1. generateMetadata — brauzer tabidagi sarlavha va Google uchun tavsif (<title>, <meta name="description">).
  2. Sahifaning o'zi — post matni.

Ikkalasi alohida funksiya. Ikkalasi ham post(slug) ni chaqiradi. Demak bitta sahifa uchun fayl ikki marta o'qiladi. Bazadan bo'lsa — ikki marta so'rov. Sahifada post muallifi bloki, "o'xshash postlar" bloki bo'lsa — yana ikki marta.

Props bilan uzatish mumkin edi, lekin generateMetadata va sahifa bir-biriga prop bera olmaydi — ularni Next.js alohida chaqiradi.

Choyxonadagi buyurtmani eslang. Bitta stol ofitsiantga uch marta "bitta choynak choy" desa, ofitsiant uch choynak olib kelmaydi — u bu bitta stolning bitta buyurtmasi ekanini biladi. Boshqa stol esa o'z choynagini oladi. cache() — shu ofitsiant: bitta so'rov ichidagi bir xil chaqiruvlarni birlashtiradi.

2. React cache()

2.1 Qanday yoziladi

cache — react paketidan. Funksiyani o'raysiz, o'ralganini ishlatasiz:

ts
import { cache } from "react";
import { post } from "./blog";

export const postniOl = cache(async (slug: string) => post(slug));

Endi bitta render davomida:

  • postniOl("salom") birinchi marta — post("salom") ishlaydi, natija eslab qolinadi;
  • postniOl("salom") ikkinchi marta — o'sha natija (o'sha Promise) qaytadi, post ishlamaydi;
  • postniOl("xayr") — boshqa argument, alohida chaqiruv.

Argumentlar Object.is bilan solishtiriladi: satr va son qiymat bo'yicha, obyekt esa havola bo'yicha. postniOl({ slug: "salom" }) ni ikki marta yozsangiz — ikkita har xil obyekt, ikkita chaqiruv. Shuning uchun cache() ga oddiy qiymatlar (satr, son) bering.

2.2 cache() va "use cache"

Nomlari o'xshash, vazifasi boshqa:

React cache() "use cache"
Qancha yashaydi Bitta so'rov (render) cacheLife muddatigacha
Kimga umumiy Faqat shu so'rovga Hamma foydalanuvchiga
Qayerda saqlanadi So'rov xotirasida Server keshida, statik qobiqda
Nima uchun Takroriy chaqiruvni birlashtirish Qayta hisoblamaslik, tez sahifa
Foydalanuvchiga xos ma'lumot Mumkin (so'rov tugashi bilan o'chadi) Taqiqlangan (cookies() — xato)

Oxirgi qator muhim. cache() bilan joriy foydalanuvchini o'qish xavfsiz: natija boshqa foydalanuvchiga hech qachon o'tmaydi. Next.js hujjatidagi DAL misoli aynan shunday — getCurrentUser = cache(async () => { … cookies() … }).

2.3 Natija oynasida: client'da cache() eslamaydi

cache() faqat Server Component muhitida ishlaydi. Natija oynasidagi React — client to'plami. Biz React 19.3.0 ning client kodini ochib ko'rdik: u yerda cache shunchaki chaqiruvni o'tkazib yuboradi (return fn.apply(null, arguments)). Sinab ko'ring:

tsx
import { cache } from "react";

let calls = 0;

const getPost = cache((slug: string) => {
  calls++;
  console.log(`tana ishladi: ${slug}`);
  return { slug, title: "Portfolio'ni Next.js'da qayta qurdim" };
});

getPost("portfolio-v2");
getPost("portfolio-v2");
console.log("chaqiruvlar:", calls);

export default function App() {
  return <p>{getPost("portfolio-v2").title}</p>;
}

Konsolda:

text
tana ishladi: portfolio-v2
tana ishladi: portfolio-v2
chaqiruvlar: 2
tana ishladi: portfolio-v2

Ekranda:

text
Portfolio'ni Next.js'da qayta qurdim

Tana uch marta ishladi — eslab qolish yo'q. Bu xato emas: client'da "bitta so'rov" tushunchasi yo'q, foydalanuvchi bilan sahifa soatlab ochiq turadi. Serverda esa xuddi shu kod har so'rovda birinchi chaqiruvni bajaradi, qolganini birlashtiradi. Shuning uchun cache() ni client komponentda ishlatmang — u yerda useMemo va memoizatsiya usullari bor.

2.4 So'rov doirasi: o'quv modeli

Serverdagi xulqni oddiy JS bilan takrorlaymiz. Har so'rov — yangi Map (React buni o'zi qiladi, biz qo'lda):

js
let bodyRuns = 0;

function readPost(slug) {
  bodyRuns++;
  return `«${slug}» matni`;
}

// Har so'rovga yangi xotira — React cache() g'oyasi
function handleRequest(name, render) {
  const memo = new Map();
  const postniOl = (slug) => {
    if (!memo.has(slug)) memo.set(slug, readPost(slug));
    return memo.get(slug);
  };
  render(postniOl);
  console.log(`${name}: tana jami ${bodyRuns} marta`);
}

const page = (postniOl) => {
  postniOl("salom"); // generateMetadata
  postniOl("salom"); // sahifa
  postniOl("salom"); // muallif bloki
};

handleRequest("1-so'rov", page);
handleRequest("2-so'rov", page);

Konsolda:

text
1-so'rov: tana jami 1 marta
2-so'rov: tana jami 2 marta

Bitta so'rov ichida uch chaqiruv — bitta o'qish. Ikkinchi so'rov o'z xotirasi bilan boshlandi va yana bir marta o'qidi. So'rovlararo saqlash — "use cache" ning ishi.

Tekshirib ko'ring: postniOl ni cache() siz, oddiy funksiya qilib qoldirsak, bitta sahifa ochilganda readPost necha marta ishlaydi?

Javob

Uch marta — generateMetadata, sahifa va muallif bloki uchun alohida. cache() bilan — bir marta.

3. Kanon o'lchovi: 4 dan 2 ga

3.1 Tajriba

Kanon postniOl va post() ichiga vaqtincha console.log hisoblagich qo'yib, next build qildi — cache() bilan va usiz (natijalar/tajriba/20-cache-dedup.txt):

text
## cache() bilan
      1 [olchov] post() tanasi: portfolio-v2
      1 [olchov] post() tanasi: server-components
      1 [olchov] post() tanasi: vazifalar-testlari
      2 [olchov] postniOl: portfolio-v2
      2 [olchov] postniOl: server-components
      2 [olchov] postniOl: vazifalar-testlari
## cache() SIZ
      1 [olchov] post() tanasi: portfolio-v2
      1 [olchov] post() tanasi: server-components
      1 [olchov] post() tanasi: vazifalar-testlari
      4 [olchov] postniOl: portfolio-v2
      4 [olchov] postniOl: server-components
      4 [olchov] postniOl: vazifalar-testlari

Chapdagi son — shu qator logda necha marta chiqqani.

Bitta post uchun build vaqtidagi chaqiruvlar
  • postniOl, cache() siz4 marta
  • postniOl, cache() bilan2 marta
  • post() tanasi (use cache)1 marta

Manba: IlmHamroh kanoni, portfolio v2 holat 20, next build, natijalar/tajriba/20-cache-dedup.txt, 2026-10-21

3.2 Raqamlarni o'qish

Nega 4? Cache Components'da har slug ikki marta prerender qilinadi (Next.js ichki tekshiruvi uchun). Har birida generateMetadata ham, sahifa ham postniOl ni chaqiradi: 2 × 2 = 4. cache() bitta render ichidagi ikkita chaqiruvni birlashtiradi: 4 → 2.

Endi uchinchi ustunga qarang: post() tanasi — haqiqiy fayl o'qish — har ikki holatda ham 1. Chunki post() ichida "use cache" bor (Cache Components I): u so'rovlararo kesh, birinchi chaqiruvdan keyin fayl qayta o'qilmaydi.

Halol xulosa: ma'lumot "use cache" ortida bo'lsa, cache() faqat o'ram chaqiruvlarini tejaydi. Fayl yoki baza baribir bir marta o'qiladi. cache() ning haqiqiy foydasi — keshlanmagan murojaatlarda: foydalanuvchiga xos baza so'rovi, joriy foydalanuvchini aniqlash, har so'rovda o'zgaradigan ma'lumot. Bular 26–28-qismlarda keladi (baza va auth). Kanon DAL'ni hozir qo'ydi — tuzilma tayyor bo'lsin.

3.3 fetch uchun bu o'zi bor

Serverda ma'lumot olish darsida aytilgan edi: bir render ichidagi bir xil fetch (manzil va sozlamalar bir xil, GET) Next.js'da avtomatik birlashtiriladi. Bu request memoization (so'rov ichidagi memoizatsiya) deyiladi. cache() — xuddi shu narsa, lekin fetch bo'lmagan funksiyalar uchun: fayl, baza, hisob-kitob.

Next.js hujjatidagi qoida: fetch — memoizatsiya o'zi; ORM yoki baza — React.cache bilan o'rang.

4. Data Access Layer

4.1 Muammo: ma'lumot hamma joyda

Kichik loyihada har sahifa ma'lumotni o'zi oladi: biri post() ni, biri loyihaniTop() ni, uchinchisi to'g'ridan faylni o'qiydi. Loyiha o'sganda savollar paydo bo'ladi:

  • Bu ma'lumotni kim ko'ra oladi? Tekshiruv qaysi faylda?
  • Sahifaga butun yozuv ketyaptimi yoki faqat kerakli maydonlar?
  • Bu modul tasodifan brauzer to'plamiga tushib qolmaydimi?

Har sahifada alohida javob — har sahifada alohida xato imkoniyati. Next.js hujjati yangi loyihalar uchun boshqa yo'lni tavsiya qiladi.

4.2 Naqsh

Data Access Layer (DAL) — ma'lumotga murojaat qiladigan yagona server modul. Hujjat uchta talab qo'yadi:

  1. Faqat serverda ishlaydi.
  2. Ruxsat (avtorizatsiya) tekshiruvini o'zi qiladi.
  3. Xavfsiz, minimal DTO qaytaradi.

DTO (Data Transfer Object — ma'lumot uzatish obyekti) — sahifaga beriladigan, faqat kerakli va ochiq maydonlardan iborat obyekt. Bazadagi yozuvda parol xeshi, telefon raqami, ichki izohlar bo'lishi mumkin — DTO'da faqat sahifa ko'rsatadigan narsa.

flowchart LR
  P1["loyihalar/[slug]/page.tsx"] --> D["dal.ts<br/>server-only, cache(), DTO"]
  P2["blog/[slug]/page.tsx"] --> D
  M["generateMetadata"] --> D
  D --> B["blog.ts: fayllar"]
  D --> L["loyihalar.ts: ro'yxat"]
  D -. "28-qism" .-> A["auth tekshiruvi"]

Diagrammada sahifalar va metadata faqat dal.ts ga qaraydi. Ma'lumot manbalari (fayl, ro'yxat, keyin baza) uning orqasida yashiringan.

4.3 Kanon dal.ts

ts
import "server-only";
import { cache } from "react";
import { post } from "./blog";
import { loyihaniTop } from "./loyihalar";

// Data Access Layer (N-20): sahifalar ma'lumotni faqat shu
// yerdan oladi.
// - `server-only`: client bundle'ga tushmaydi;
// - React `cache()`: BITTA so'rov (render) ichida
//   generateMetadata va sahifa bir xil chaqiruvni bo'lishadi;
// - DTO: sahifaga faqat kerakli maydonlar (keyin auth
//   qo'shilsa — tekshiruv ham shu yerga, 28-qism).

export const postniOl = cache(async (slug: string) => post(slug));

export const loyihaniOl = cache(async (slug: string) => {
  const l = loyihaniTop(slug);
  if (!l) return null;
  const { repo, ...ochiq } = l; // repo — ichki maydon
  return { ...ochiq, repoUrl: `https://github.com/${repo}` };
});

Kanon izohlari telefonga sig'ishi uchun bo'lindi; repo qatoridagi izoh qisqartirildi (aslida: "repo — ichki maydon (statistika uchun), sahifaga kerak emas").

Qator Ma'nosi
import "server-only" Client komponent import qilsa — build xatosi
cache(async (slug) => post(slug)) Bitta render ichida bir marta
const { repo, ...ochiq } = l Rest bilan repo ni ajratib tashlash
repoUrl: … Sahifaga tayyor havola — ichki format emas

repo — "octocat/vazifalar" ko'rinishidagi ichki qiymat. Sahifa unga muhtoj emas, faqat havola kerak. Bugun bu xavfsizlik masalasi emas (repo nomi ochiq), lekin odat muhim: DAL nima chiqishini hal qiladi, sahifa emas.

4.4 Natija oynasida: DTO

DTO — oddiy funksiya, uni alohida sinash oson:

ts
type Project = {
  slug: string;
  nomi: string;
  repo: string;
  ichkiIzoh: string;
};

type ProjectDto = Omit<Project, "repo" | "ichkiIzoh"> & {
  repoUrl: string;
};

function toDto(p: Project): ProjectDto {
  const { repo, ichkiIzoh, ...open } = p;
  return { ...open, repoUrl: `https://github.com/${repo}` };
}

const project: Project = {
  slug: "vazifalar",
  nomi: "Vazifalar",
  repo: "octocat/vazifalar",
  ichkiIzoh: "mijoz bilan narx kelishilmagan",
};

console.log(toDto(project));

Konsolda:

text
{
  slug: 'vazifalar',
  nomi: 'Vazifalar',
  repoUrl: 'https://github.com/octocat/vazifalar'
}

ichkiIzoh sahifaga umuman yetib bormadi. Omit (utility turlar) qaytish turini ham shunga moslaydi: sahifa kodida dto.ichkiIzoh deb yozsangiz — TypeScript xatosi. Bu muhim, chunki Server Component client komponentga prop bergan har bir maydon RSC payload orqali brauzerga ketadi (Server Components) — "faqat sahifada ishlataman" degan maydon ham.

4.5 server-only — qo'riqchi

import "server-only" — kichik paket, uning yagona vazifasi: fayl client komponent daraxtiga import qilinsa, build'ni to'xtatish. Kanon buni tajribada ko'rgan (Server va Client kompozitsiyasi, xom natijalar/tajriba/11-server-only-xato.txt):

text
Error: You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.

Xabarning ikkinchi yarmi chalg'ituvchi: kanonda Pages Router yo'q edi. Asosiysi — build to'xtadi va sir modul brauzerga ketmadi.

4.6 Ruxsat tekshiruvi ma'lumot yonida

Portfolio'da hamma narsa ochiq — tekshiradigan narsa yo'q. Lekin 28-qismda login paydo bo'lganda tekshiruv DAL ichiga qo'yiladi, sahifaga emas. Sxema (hozir ishlamaydi — auth hali yo'q):

ts
// dal.ts — 28-qismdan keyin (sxema)
export const vazifalarniOl = cache(async () => {
  const foydalanuvchi = await joriyFoydalanuvchi(); // cache() bilan
  if (!foydalanuvchi) return null; // yoki redirect("/kirish")
  const royxat = await db.vazifa.findMany({
    where: { egasiId: foydalanuvchi.id }, // faqat o'ziniki
  });
  return royxat.map((v) => ({ id: v.id, matn: v.matn }));
});

Nega sahifada emas? Layout yoki sahifadagi tekshiruvni chetlab o'tish mumkin: masalan, ma'lumot boshqa yo'l bilan — Route Handler, Server Action yoki boshqa sahifa orqali olinsa. Tekshiruv ma'lumotning o'zida bo'lsa, qayerdan chaqirilmasin, u ishlaydi. Next.js hujjati ham layout'da auth tekshirishdan ogohlantiradi: layout navigatsiyada qayta chizilmaydi (Layout va template).

4.7 Hujjatdagi qo'shimcha himoya: taint

React'da tajribaviy API bor — experimental_taintObjectReference va experimental_taintUniqueValue. Ular obyektni yoki qiymatni (masalan token) "brauzerga o'tmasin" deb belgilaydi: tasodifan client komponentga prop qilib berilsa — xato. Next.js'da experimental.taint sozlamasi bilan yoqiladi. Nomidagi experimental — API hali barqaror emas. Kanon uni ishlatmadi: DTO yetarli. Biladigan bo'lib qo'ying — katta loyihalarda uchraydi.

5. generateMetadata va DAL

Kanon DAL bilan birga loyiha va post sahifalariga generateMetadata qo'shdi — har sahifa o'z sarlavhasini oladi:

tsx
// app/(sayt)/blog/[slug]/page.tsx
import type { Metadata } from "next";
import { postniOl } from "@/malumot/dal";

export async function generateMetadata({
  params,
}: PageProps<"/blog/[slug]">): Promise<Metadata> {
  const p = await postniOl((await params).slug);
  return p
    ? { title: `${p.sarlavha} — Aziz Karimov`, description: p.tavsif }
    : {};
}

export default async function PostSahifasi({
  params,
}: PageProps<"/blog/[slug]">) {
  const p = await postniOl((await params).slug);
  if (!p) notFound();
  // …
}

Ikkalasi postniOl ni chaqiradi — cache() tufayli bir render ichida bitta chaqiruv. generateMetadata va Metadata turi — Metadata API darsida to'liq; bugun faqat DAL'dan foydalanuvchi sifatida.

6. cache() tuzoqlari

Tuzoq Nima bo'ladi Davosi
cache() ni komponent ichida chaqirish Har render yangi kesh — hech narsa birlashmaydi Modul darajasida bir marta: export const x = cache(…)
Obyekt argument Har chaqiruv yangi obyekt — boshqa kalit Satr/son argument
Client komponentda Eslamaydi (natija oynasidagidek) Server kodida ishlating
"use cache" ichiga cache() orqali qiymat uzatish Ko'rinmaydi — alohida doira (hujjat: "React.cache isolation") Argument sifatida bering
Xato tashlagan chaqiruv Xato ham eslanadi (shu so'rov ichida) Xatoni ichkarida ushlang yoki qabul qiling

7. Portfolio qadami: DAL va commit

7.1 Fayllar

Fayl O'zgarish
src/malumot/dal.ts Yangi: server-only, postniOl, loyihaniOl (DTO: repo → repoUrl)
(sayt)/blog/[slug]/page.tsx post() o'rniga postniOl(); yangi generateMetadata
(sayt)/loyihalar/[slug]/page.tsx loyihaniTop() o'rniga loyihaniOl(); generateMetadata; havola loyiha.repoUrl

Loyiha sahifasidagi o'zgarish:

diff
-  <a href={`https://github.com/${loyiha.repo}`}>GitHub'dagi kod</a>
+  <a href={loyiha.repoUrl}>GitHub'dagi kod</a>

Ro'yxat sahifalari (/blog, /loyihalar) hozircha postlar() va LOYIHALAR ni to'g'ridan ishlatadi — kanon DAL'ni bitta yozuvli sahifalardan boshladi.

7.2 Tekshiruv va commit

npm run build — jadval o'zgarmadi (○ sahifalar, blog 30d 1y). Brauzerda: post sahifasining tabida endi post sarlavhasi. Kanon o'lchovi: JS o'zgarmadi, axe 0, hydration 0.

bash
git add -A
git commit

Commit (kanon): DAL: malumot/dal.ts (server-only, cache(), DTO); loyiha va post sahifalariga generateMetadata.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Bitta sahifada bir xil so'rov bir necha marta Baza/fayl funksiyasi cache() siz cache() bilan o'rang
cache() bor, lekin baribir ko'p chaqiruv Obyekt argument yoki komponent ichida yaratilgan Oddiy argument, modul darajasi
Client komponentda cache() "ishlamaydi" Client'da u o'tkazib yuboradi Server kodiga ko'chiring
…depends on "server-only"… build xatosi DAL client faylga import qilingan Ma'lumotni Server Component'da oling, prop bilan bering
Brauzerga keraksiz maydonlar ketdi Butun yozuv prop qilib berilgan DTO
Ruxsat tekshiruvini chetlab o'tishdi Tekshiruv faqat sahifa yoki layout'da Tekshiruvni DAL ichiga
cache() va "use cache" chalkashdi Biri so'rov ichida, biri so'rovlararo «cache() va "use cache"» jadvali

9. Mashqlar

Mashqlar kurs/mashqlar/23/20-dal/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): qaysi biri?

  • Joriy foydalanuvchini bitta so'rov ichida 5 ta komponent so'raydi:
  • Kurs ro'yxati hamma uchun bir xil, kuniga bir o'zgaradi:
  • Foydalanuvchining savatchasi (cookie bo'yicha), bitta sahifada 3 joyda:
  • Bir xil fetch URL bitta render'da 2 marta: hech narsa — Next.js o'zi qiladi
Yechim
  • cache() — foydalanuvchiga xos, faqat shu so'rov ichida.
  • use cache — hamma uchun bir xil, so'rovlararo saqlanadi.
  • cache() — savatcha shaxsiy; "use cache" ichida cookies() taqiqlangan.
  • memoizatsiya — bir xil fetch lar avtomatik birlashtiriladi.

2-mashq (o'rta): foydalanuvchi DTO'si

Bazadagi foydalanuvchi: { id, ism, email, telefon, parolXeshi, rol }. userDto(user, viewer) funksiyasini yozing: hamma ko'radi — id, ism; email — faqat o'zi yoki admin ko'radi (aks holda null); telefon — faqat o'zi; parolXeshi — hech qachon. Uchta viewer bilan sinang: o'zi, admin, begona.

Yechim
js
function userDto(user, viewer) {
  const self = viewer.id === user.id;
  const admin = viewer.rol === "admin";
  return {
    id: user.id,
    ism: user.ism,
    email: self || admin ? user.email : null,
    telefon: self ? user.telefon : null,
  };
}

const user = {
  id: 7,
  ism: "Malika",
  email: "malika@example.com",
  telefon: "+998 90 000 00 00",
  parolXeshi: "$argon2id$…",
  rol: "talaba",
};

console.log(userDto(user, { id: 7, rol: "talaba" }));
console.log(userDto(user, { id: 1, rol: "admin" }));
console.log(userDto(user, { id: 9, rol: "talaba" }));

Konsolda:

text
{
  id: 7,
  ism: 'Malika',
  email: 'malika@example.com',
  telefon: '+998 90 000 00 00'
}
{ id: 7, ism: 'Malika', email: 'malika@example.com', telefon: null }
{ id: 7, ism: 'Malika', email: null, telefon: null }

DTO'ni "nima olib tashlansin" (rest bilan) emas, "nima qo'shilsin" deb qurish xavfsizroq: bazaga yangi maxfiy maydon qo'shilsa, u avtomatik DTO'ga tushib qolmaydi. Kanondagi { repo, ...ochiq } usuli qisqa, lekin shu xavfni o'zida saqlaydi — ochiq ma'lumotli kichik ro'yxat uchun qabul qilinadi.

3-mashq (qiyin): so'rov doirasidagi memo

«So'rov doirasi» modelini universal qiling: createRequestCache() — ichida Map bo'lgan obyekt qaytaradi va wrap(fn) metodi bor: wrap(fn) qaytargan funksiya birinchi argument bo'yicha natijani eslaydi. Ikki xil funksiya (readPost, readProject) bitta so'rovda bir xil argument bilan chaqirilsa, natijalar aralashmasligi kerak (har wrap — o'z Map i). Ikki so'rovni simulyatsiya qiling va har funksiya tanasi necha marta ishlaganini chiqaring.

Yechim
js
const runs = { post: 0, project: 0 };

function createRequestCache() {
  return {
    wrap(fn) {
      const memo = new Map();
      return (key) => {
        if (!memo.has(key)) memo.set(key, fn(key));
        return memo.get(key);
      };
    },
  };
}

function readPost(slug) {
  runs.post++;
  return `post:${slug}`;
}

function readProject(slug) {
  runs.project++;
  return `loyiha:${slug}`;
}

for (const name of ["1-so'rov", "2-so'rov"]) {
  const req = createRequestCache();
  const getPost = req.wrap(readPost);
  const getProject = req.wrap(readProject);
  getPost("vazifalar");
  getPost("vazifalar");
  console.log(getProject("vazifalar"), getPost("vazifalar"));
  console.log(name, runs);
}

Konsolda:

text
loyiha:vazifalar post:vazifalar
1-so'rov { post: 1, project: 1 }
loyiha:vazifalar post:vazifalar
2-so'rov { post: 2, project: 2 }

Har wrap o'z Map iga ega — "vazifalar" kaliti ikki funksiyada aralashmadi. React cache() ham har o'ralgan funksiya uchun alohida xotira tutadi va har so'rov boshida uni yangilaydi.

4-mashq: Portfolio qadami

src/malumot/dal.ts ni yarating, loyiha va post sahifalarini DAL'ga o'tkazing, ikkalasiga generateMetadata qo'shing. npm run build toza bo'lsin. Tajriba: dal.ts ni biror client komponentga import qilib ko'ring — server-only xatosini ko'ring va qaytaring. Keyin commit.

Yechim

Namuna — «Kanon dal.ts» va «generateMetadata va DAL» bo'limlaridagi kod. Tekshirish: /blog/portfolio-v2 tabida «Portfolio'ni Next.js'da qayta qurdim — Aziz Karimov». Loyiha sahifasidagi «GitHub'dagi kod» havolasi https://github.com/… ga olib boradi. Commit xabari — kanondagi.

10. Real ishda

  • DAL — Next.js jamoasining yangi loyihalar uchun rasmiy tavsiyasi. Katta jamoalarda data/ yoki lib/dal/ papkasi va "sahifa bazaga to'g'ridan murojaat qilmaydi" qoidasi.
  • DTO — backend'da ham asosiy naqsh (NestJS'da 25+ qismlar): API javobida parol xeshi yoki ichki ID chiqib ketishi — real xavfsizlik hodisalarining tez-tez sababi.
  • cache() — auth bilan birga eng ko'p ishlatiladi: getCurrentUser() bir sahifada 10 joyda chaqiriladi, sessiya esa bir marta tekshiriladi.
  • Intervyuda: "React.cache va Next.js keshi farqi?", "Server Component'dan client'ga nima ketadi?".

Xulosa

  • cache() bitta so'rov ichida bir xil chaqiruvlarni birlashtiradi; "use cache" — so'rovlararo kesh.
  • Kanon: cache() chaqiruvlarni 4 → 2 ga tushirdi; fayl o'qish "use cache" tufayli baribir 1 marta.
  • cache() ning asosiy foydasi — keshlanmagan, foydalanuvchiga xos so'rovlarda; client'da u hech narsani eslamaydi.
  • DAL — yagona server modul: server-only, cache(), DTO va ruxsat tekshiruvi ma'lumot yonida.
  • DTO sahifaga faqat kerakli maydonlarni beradi — client'ga prop bo'lgan har maydon brauzerga ketadi.

Keyingi dars: Server Actions I: "use server" va formalar — «Aloqa» formasi serverga to'g'ridan yozadi, hatto JavaScript'siz ham.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
React cache() va Data Access Layer: Next.js'da ma'lumot qatlami — IlmHamroh