IlmHamroh
JavaScript Full-stack/23-qism. Next.js28/43-dars16 daqiqa
Mundarija (28)

TanStack Query va Next.js: prefetchQuery, dehydrate va HydrationBoundary

Qisqacha: Next.js'da ma'lumotning ko'p qismini Server Component o'zi oladi — Query kerak emas. Query client tomonda "jonli" ma'lumot uchun kerak: filtr bilan qayta so'rash, cheksiz ro'yxat, polling, optimistik yangilash. Ikkalasini ulash naqshi: serverda prefetchQuery bilan keshni to'ldirasiz, dehydrate() bilan uni oddiy obyektga aylantirasiz, <HydrationBoundary state={...}> client keshiga qaytarib qo'yadi. Shunda HTML'da ma'lumot tayyor, brauzer esa qayta so'ramaydi (agar staleTime > 0).

Bu darsda

  • Server Component'dagi await va client'dagi useQuery qachon nimaga kerakligini jadval bilan ajrata olasiz.
  • prefetchQuery → dehydrate → HydrationBoundary zanjirini tushuntirasiz va natija oynasida jonli ko'rasiz.
  • staleTime: 0 nega sahifa ochilishi bilan ikkinchi so'rov yuborishini ko'rasiz.
  • Serverda nega har so'rovga yangi QueryClient kerakligini (aks holda foydalanuvchilar ma'lumoti aralashadi) bilasiz.
  • portfolio ga Query nega kirmaganini kanon qarori bilan asoslay olasiz.

Oldin bilishingiz kerak: TanStack Query V: prefetch va Suspense, Server state va client state, Serverda ma'lumot olish, Provider'lar App Router'da, Streaming va Suspense.

1. Nega bu kerak?

19-qismda vazifalar ma'lumotni TanStack Query bilan oldi: useQuery, kesh, staleTime, mutatsiyalar. O'shanda SPA edi — hamma narsa brauzerda. Next.js'da esa ma'lumot olishning yana bir yo'li paydo bo'ldi: Server Component'da oddiy await:

tsx
export default async function BlogSahifasi() {
  const royxat = await postlar();
  return <PostRoyxati postlar={royxat} />;
}

Kesh yo'q, isLoading yo'q, provider yo'q. HTML brauzerga ma'lumot bilan tayyor keladi. Shunda savol tug'iladi: Next'da Query umuman kerakmi?

Javob: ba'zan. Server Component bir marta chizadi — sahifa ochilganda. Undan keyin ma'lumot o'zgarishi kerak bo'lsa (foydalanuvchi filtr bosdi, ro'yxat pastga aylantirildi, narx har 10 soniyada yangilanadi), brauzerda kim so'raydi? Bu yerda Query yana o'z ishiga qaytadi.

Hayotiy o'xshatish: restoran. Server Component — oshxonadan tayyor taom olib chiqqan ofitsiant: stolga qo'ydi, ketdi. Query — stolingizdagi samovar: choy tugasa, o'zi to'ldiradi; kerak bo'lsa yangisini damlaydi. Eng yaxshi tajriba — ikkalasi birga: ofitsiant birinchi choynakni to'la olib keladi (server prefetch), keyin samovar davom ettiradi (client Query).

2. Ikki dunyo: RSC va Query

2.1 Taqqoslash

Server Component await Client useQuery
Qayerda ishlaydi serverda, render paytida brauzerda
HTML'da ma'lumot bor yo'q (faqat prefetch bilan)
Brauzer JS'i 0 KB Query ~10 KB gzip + komponent
Sahifadan keyin yangilash sahifani qayta so'rash (router.refresh) o'zi: qayta fokus, interval, kalit o'zgarishi
Kesh Next keshi ("use cache") brauzer xotirasidagi Query keshi
Kalit/sir serverda xavfsiz brauzerga ketmasligi kerak

~10 KB — 19-qism o'lchovi: vazifalar ga Query qo'shilganda birinchi yuklash +9.9 KB gzip.

2.2 Qaror savollari

Next hujjati (Backend for Frontend qo'llanmasi) client'da ma'lumot olish kerak bo'ladigan holatlarni aniq sanaydi: faqat brauzerda bor API'larga bog'liq ma'lumot (geolokatsiya, localStorage, fayl) va tez-tez so'raladigan (polling) ma'lumot. Bunga TanStack Query hujjati yana ikkitasini qo'shadi: cheksiz ro'yxat va mutatsiyadan keyin optimistik yangilash.

flowchart TB
  Q{"Ma'lumot sahifa ochilgandan keyin o'zgaradimi?"}
  Q -->|yo'q| R["Server Component await"]
  Q -->|ha| U{"Foydalanuvchi harakati bilanmi?"}
  U -->|"forma, tugma"| A["Server Action + revalidate"]
  U -->|"filtr, scroll, polling"| QQ["TanStack Query"]

O'rta tarmoq muhim: forma yuborilgandan keyin ro'yxatni yangilash uchun Query shart emas. Server Action ma'lumotni o'zgartiradi, revalidatePath/updateTag esa sahifani yangilaydi (Server Actions II). Query — foydalanuvchi ko'p va tez o'zgartiradigan ko'rinishlar uchun.

Tekshirib ko'ring: portfolio dagi GitHub bloki (repo soni) — RSC'mi yoki Query'mi? Ma'lumot soatda bir o'zgaradi.

Javob

RSC. Sahifa ochilgandan keyin foydalanuvchi uni o'zgartirmaydi, soniyalik aniqlik ham kerak emas. Kanon uni serverda "use cache" + cacheLife bilan oladi (Cache Components I): brauzer GitHub'ga umuman bormaydi va JS qo'shilmaydi.

3. Muammo: SSR'da useQuery

Agar Next sahifasida client komponent oddiy useQuery ishlatsa nima bo'ladi?

  1. Server sahifani chizadi. useQuery serverda ham ishlaydi, lekin u so'rov yubormaydi — HTML'ga isPending holati, ya'ni «Yuklanmoqda…» tushadi.
  2. Brauzer HTML'ni ko'rsatadi — skelet.
  3. JS yuklanadi, hydration bo'ladi, endi so'rov ketadi.
  4. Javob keladi, ro'yxat chiziladi.

Ya'ni SPA'dagi "bo'sh vitrina" (Next.js nima) qaytib keldi — Next'dan foydalanishning ma'nosi qolmaydi. Kerakli narsa: server ma'lumotni o'zi olsin, HTML'ga qo'ysin va Query keshiga ham uzatsin — brauzer qayta so'ramasin.

4. Naqsh: prefetch → dehydrate → HydrationBoundary

4.1 Uch qadam

sequenceDiagram
  participant SC as Server Component
  participant QC as QueryClient (server)
  participant B as Brauzer
  SC->>QC: prefetchQuery(["loyihalar"])
  QC-->>SC: kesh to'ldi
  SC->>B: HTML + dehydrate(QC) (JSON)
  Note over B: HydrationBoundary keshga qo'yadi
  B->>B: useQuery(["loyihalar"]) — keshdan, so'rovsiz
Qadam Qayerda Nima qiladi
prefetchQuery Server Component so'raydi va server keshiga yozadi
dehydrate(queryClient) Server Component keshni oddiy JSON obyektga aylantiradi
<HydrationBoundary state> client komponent (o'zi "use client") obyektni brauzer keshiga qo'yadi

Dehydrate ("suvsizlantirish") — keshni brauzerga jo'natish mumkin bo'lgan oddiy obyektga aylantirish. Hydrate — uni qayta jonli keshga aylantirish. React'ning hydration so'zi bilan bir oila: serverda tayyorlangan narsa brauzerda "jonlanadi".

4.2 Next'dagi kod

TanStack Query hujjatidagi naqsh, portfolio nomlari bilan (kanonda yo'q — tushuntirish uchun):

tsx
// app/loyihalar/page.tsx — Server Component
import {
  dehydrate,
  HydrationBoundary,
  QueryClient,
} from "@tanstack/react-query";
import { LoyihaRoyxati } from "./_komponentlar/LoyihaRoyxati";
import { loyihalarniOl } from "@/malumot/dal";

export default async function LoyihalarSahifasi() {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({
    queryKey: ["loyihalar"],
    queryFn: loyihalarniOl,
  });
  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <LoyihaRoyxati />
    </HydrationBoundary>
  );
}
tsx
// _komponentlar/LoyihaRoyxati.tsx
"use client";
import { useQuery } from "@tanstack/react-query";

export function LoyihaRoyxati() {
  const { data } = useQuery({
    queryKey: ["loyihalar"],
    queryFn: () => fetch("/api/loyihalar").then((r) => r.json()),
  });
  return <ul>{data?.map((l) => <li key={l.slug}>{l.nomi}</li>)}</ul>;
}

Ikki narsaga qarang:

  • Kalit bir xil — ["loyihalar"]. Client useQuery keshdan aynan shu kalit bo'yicha topadi. Kalit farq qilsa, prefetch behuda.
  • queryFn lar boshqa. Serverda — to'g'ridan DAL (HTTP'siz, BFF darsidagi qoida). Brauzerda — Route Handler, chunki brauzer DAL'ga kira olmaydi. Keyinchalik qayta so'rash kerak bo'lsa, client'ning queryFn i ishlaydi.

Qolgan qismi — provider. QueryClientProvider — Context, ya'ni client komponent. Uni Provider'lar darsidagi kabi alohida "use client" faylga qo'yib, root layout'da o'raysiz.

4.3 Natija oynasida: dehydrate va HydrationBoundary

Natija oynasida server yo'q, lekin @tanstack/react-query bor. "Server" qismini qo'lda taqlid qilamiz: alohida QueryClient ni setQueryData bilan to'ldiramiz (bu prefetch natijasining o'zi), dehydrate qilamiz va client'ga HydrationBoundary orqali beramiz. queryFn chaqirilsa — konsolga "tarmoq so'rovi" yoziladi:

tsx
import {
  QueryClient,
  QueryClientProvider,
  HydrationBoundary,
  dehydrate,
  useQuery,
} from "@tanstack/react-query";

// --- "server": prefetch natijasi ---
const serverClient = new QueryClient();
serverClient.setQueryData(["loyihalar"], ["Vazifalar", "Bahor"]);
const holat = dehydrate(serverClient);
const yuklama = holat.queries[0].state.data;
console.log("dehydrate:", JSON.stringify(yuklama));

// --- "brauzer" ---
const browserClient = new QueryClient({
  defaultOptions: { queries: { staleTime: 60_000 } },
});

function ProjectList() {
  const { data } = useQuery({
    queryKey: ["loyihalar"],
    queryFn: async () => {
      console.log("tarmoq so'rovi");
      return ["Vazifalar", "Bahor", "Landing"];
    },
  });
  return <ul>{data?.map((n) => <li key={n}>{n}</li>)}</ul>;
}

export default function App() {
  return (
    <QueryClientProvider client={browserClient}>
      <HydrationBoundary state={holat}>
        <ProjectList />
      </HydrationBoundary>
    </QueryClientProvider>
  );
}

Konsolda:

text
dehydrate: ["Vazifalar","Bahor"]

Ekranda:

text
Vazifalar
Bahor

"tarmoq so'rovi" yo'q: useQuery ma'lumotni keshdan oldi, staleTime ichida yangi deb hisobladi va so'ramadi. Ekranda ikki loyiha — "server" bergani. dehydrate qatori ko'rsatadi: brauzerga ketadigan narsa — oddiy JSON.

4.4 Tuzoq: staleTime: 0

Endi xuddi shu blokda faqat bitta narsani o'zgartiramiz — staleTime ni olib tashlaymiz (sukut — 0):

tsx
import {
  QueryClient,
  QueryClientProvider,
  HydrationBoundary,
  dehydrate,
  useQuery,
} from "@tanstack/react-query";

const serverClient = new QueryClient();
serverClient.setQueryData(["loyihalar"], ["Vazifalar", "Bahor"]);
const holat = dehydrate(serverClient);

const browserClient = new QueryClient(); // staleTime: 0

function ProjectList() {
  const { data } = useQuery({
    queryKey: ["loyihalar"],
    queryFn: async () => {
      console.log("tarmoq so'rovi");
      return ["Vazifalar", "Bahor", "Landing"];
    },
  });
  return <ul>{data?.map((n) => <li key={n}>{n}</li>)}</ul>;
}

export default function App() {
  return (
    <QueryClientProvider client={browserClient}>
      <HydrationBoundary state={holat}>
        <ProjectList />
      </HydrationBoundary>
    </QueryClientProvider>
  );
}

Konsolda:

text
tarmoq so'rovi

Ekranda:

text
Vazifalar
Bahor
Landing

Ma'lumot keshda bor edi, lekin staleTime: 0 da u darhol eskirgan. Komponent chizilishi bilan Query uni fonda qayta so'radi — ekranda uchinchi loyiha paydo bo'ldi. Haqiqiy Next'da bu: server allaqachon olgan ma'lumotni brauzer sahifa ochilishi bilan yana so'raydi. Ikki so'rov — bittasi ortiqcha. TanStack Query hujjati SSR uchun aynan shuni tavsiya qiladi: staleTime ni 0 dan katta qiling (odatda 60 soniya). Bu RE-18 dagi prefetch tuzog'ining SSR versiyasi.

Tekshirib ko'ring: Server ["loyihalar"] ni prefetch qildi, client useQuery({ queryKey: ["loyihalar", "barchasi"] }) ishlatadi. Nima bo'ladi?

Javob

Kalitlar mos emas — kesh topilmaydi. Client ["loyihalar", "barchasi"] uchun noldan so'raydi, HTML'da esa «Yuklanmoqda…» chiqadi. Server prefetch qilgan ma'lumot JSON bo'lib brauzerga baribir ketgan — ya'ni ham ortiqcha bayt, ham ortiqcha so'rov. Kalitlarni bitta joyda, queryOptions bilan e'lon qilish shu xatodan saqlaydi (TanStack Query II).

5. QueryClient: serverda har so'rovga yangi

5.1 Xavfli xato

SPA'da QueryClient ni modul darajasida bir marta yaratardingiz:

tsx
// ❌ serverda xavfli
const queryClient = new QueryClient();

Brauzerda bu to'g'ri — bitta foydalanuvchi, bitta kesh. Server esa hamma foydalanuvchilarga xizmat qiladi va modul darajasidagi o'zgaruvchi so'rovlar orasida yashaydi. Ali o'z buyurtmalarini prefetch qildi — ular umumiy keshga yozildi. Keyingi so'rovda Malikaning sahifasi o'sha keshni dehydrate qilib, Alining ma'lumotini ham jo'natadi.

Buni oddiy JS bilan ko'raylik — "server" ikki so'rovni ketma-ket ishlaydi:

js
// ❌ bitta kesh hamma so'rovlarga
const umumiyKesh = new Map();

function sahifaChiz(foydalanuvchi, buyurtmalar) {
  umumiyKesh.set(`buyurtmalar:${foydalanuvchi}`, buyurtmalar);
  return [...umumiyKesh.keys()];
}

console.log("Ali:", sahifaChiz("ali", ["osh"]));
console.log("Malika:", sahifaChiz("malika", ["manti"]));

Konsolda:

text
Ali: [ 'buyurtmalar:ali' ]
Malika: [ 'buyurtmalar:ali', 'buyurtmalar:malika' ]

Malikaning sahifasiga ketadigan keshda Alining kaliti ham bor. Haqiqiy loyihada dehydrate uni JSON qilib Malikaning brauzeriga yuborardi.

5.2 To'g'ri naqsh: getQueryClient

TanStack Query hujjatining App Router uchun naqshi — serverda har safar yangi, brauzerda bitta:

tsx
import { isServer, QueryClient } from "@tanstack/react-query";

function yangiClient() {
  return new QueryClient({
    defaultOptions: { queries: { staleTime: 60 * 1000 } },
  });
}

let browserClient: QueryClient | undefined;

export function getQueryClient() {
  if (isServer) return yangiClient(); // har so'rov — o'z keshi
  browserClient ??= yangiClient(); // brauzer — bitta
  return browserClient;
}

isServer — Query'ning o'zi beradigan bayroq. Brauzerdagi singleton ham muhim: React Suspense paytida komponentni qayta yaratsa, kesh yo'qolmasligi kerak. Server Component'larda esa React'ning cache() bilan bitta so'rov ichida bitta client olish mumkin — const getQueryClient = cache(() => new QueryClient()): so'rovlar orasida baham ko'rilmaydi.

6. Cheksiz ro'yxat va oqim

6.1 prefetchInfiniteQuery

Cheksiz ro'yxatda (TanStack Query IV) birinchi sahifa serverda, keyingilari brauzerda:

tsx
await queryClient.prefetchInfiniteQuery({
  queryKey: ["postlar"],
  queryFn: ({ pageParam }) => postlarSahifasi(pageParam),
  initialPageParam: 0,
});

Client'da — odatdagi useInfiniteQuery bir xil kalit bilan va IntersectionObserver bilan fetchNextPage. Foydasi: birinchi 10 ta post HTML'da (Google ko'radi, foydalanuvchi darhol o'qiydi), qolganlari aylantirilganda.

6.2 Kutmasdan jo'natish (ko'rsatkich)

Yuqoridagi naqshda Server Component await prefetchQuery ni kutadi — sekin so'rov butun sahifani ushlab turadi. Query 5 da yanada ilg'or yo'l bor: await siz prefetch, dehydrate ning shouldDehydrateQuery sozlamasi bilan hali tugamagan so'rovni ham jo'natish, client'da esa useSuspenseQuery u tugashini kutadi. Bu Streaming bilan birga ishlaydi. Hozircha bilish yetarli: bunday imkoniyat bor, hujjatda "Advanced Server Rendering" bo'limida.

7. portfolio ga Query nega kirmadi

Bu dars uchun kanonda holat yo'q. Kanon qarori — o'lchovsiz, mantiq bilan:

portfolio dagi ma'lumot Kanondagi yechim Query bilan nima o'zgarardi
GitHub profili serverda "use cache" (N-18) +JS, brauzer GitHub'ga boradi yoki BFF kerak
Aloqa formasi Server Action (N-21) useMutation + Route Handler — JS'siz ishlamaydi
Loyihalar filtri URL'da ?teg= (N-06) filtr keshda, havolani ulashib bo'lmaydi
Blog build'da Markdown'dan hech narsa — ma'lumot o'zgarmaydi

Hech bir joyda "sahifa ochilgandan keyin brauzerda qayta so'rash" kerak emas. Query qo'shilsa, birinchi yuklashga ~10 KB gzip (19-qism o'lchovi: +9.9 KB) foydasiz qo'shilardi — yakuniy portfolio ning eng og'ir sahifasi 155.3 KB, ya'ni deyarli +7%. Kanon portfolio uchun alohida o'lchamadi; shuning uchun aniq raqamni sizning loyihangiz ko'rsatadi.

Saboq: kutubxona "yaxshi" bo'lgani uchun emas, muammo bo'lgani uchun qo'shiladi. Query — vazifalar kabi interaktiv ilovada zo'r. Kontent saytida — ko'pincha ortiqcha.

Maslahat: Agar vazifalar ni kelajakda Next'ga ko'chirsangiz (vazifalar ro'yxati, filtrlar, tez-tez qo'shish/o'chirish), aynan shu darsdagi naqsh kerak bo'ladi: birinchi ro'yxat serverda, keyingi o'zgarishlar Query va mutatsiyalar bilan.

8. Qachon qaysi — yakuniy jadval

Vazifa Yechim
Sahifa ochilganda bir marta, hamma uchun bir xil Server Component + "use cache"
Har foydalanuvchiga o'z ma'lumoti, bir marta Server Component + <Suspense>
Forma yuborib, sahifani yangilash Server Action + revalidatePath / updateTag
Filtr/qidiruv natijasi (havola bilan ulashiladi) searchParams + Server Component
Cheksiz ro'yxat, polling, optimistik UI Query: server prefetch + HydrationBoundary
Faqat brauzer API'siga bog'liq (geolokatsiya) Client komponent, Query yoki useEffect

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
HTML'da «Yuklanmoqda…», ma'lumot keyin Faqat client useQuery, prefetch yo'q Serverda prefetchQuery + HydrationBoundary
Sahifa ochilishi bilan qayta so'rov staleTime: 0 staleTime: 60_000 (yoki mos qiymat)
Prefetch ishlamaydi Server va client kalitlari farq qiladi queryOptions bilan bitta e'lon
Bir foydalanuvchi boshqasining ma'lumotini ko'radi Serverda modul darajasidagi QueryClient getQueryClient(): serverda har so'rovga yangi
No QueryClient set, use QueryClientProvider to set one Provider yo'q yoki Server Component'da useQuery "use client" provider + client komponentda hook
Server Component o'z /api sini prefetch qiladi HTTP aylanma yo'l, build'da server yo'q Serverda queryFn — DAL

10. Mashqlar

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

1-mashq (oson): RSC yoki Query?

  • Blog posti matni —
  • Har 5 soniyada yangilanadigan valyuta kursi —
  • Pastga aylantirganda yuklanadigan izohlar ro'yxati —
  • «Men haqimda» sahifasidagi bilimlar ro'yxati —
Yechim
  • RSC — matn sahifa ochilgandan keyin o'zgarmaydi.
  • Query — polling (refetchInterval), brauzer o'zi yangilaydi.
  • Query — cheksiz ro'yxat: birinchi sahifa serverda prefetch, qolgani useInfiniteQuery.
  • RSC — statik matn, JS kerak emas.

2-mashq (o'rta): ikki kalit

«dehydrate va HydrationBoundary» blokini o'zgartiring: "server" ikkita kalitni to'ldirsin — ["loyihalar"] va ["postlar"] (["Server Components", "Portfolio v2"]). Client'da ikkita komponent: ProjectList va PostList. Ikkalasi ham so'rovsiz chizilishini konsol bilan tasdiqlang (har queryFn ichida log bo'lsin).

Yechim
tsx
import {
  QueryClient,
  QueryClientProvider,
  HydrationBoundary,
  dehydrate,
  useQuery,
} from "@tanstack/react-query";

const serverClient = new QueryClient();
serverClient.setQueryData(["loyihalar"], ["Vazifalar", "Bahor"]);
serverClient.setQueryData(["postlar"], [
  "Server Components",
  "Portfolio v2",
]);
const holat = dehydrate(serverClient);
console.log("kalitlar:", holat.queries.length);

const browserClient = new QueryClient({
  defaultOptions: { queries: { staleTime: 60_000 } },
});

function List({ k }: { k: string }) {
  const { data } = useQuery<string[]>({
    queryKey: [k],
    queryFn: async () => {
      console.log(`tarmoq so'rovi: ${k}`);
      return [];
    },
  });
  return <ul>{data?.map((n) => <li key={n}>{n}</li>)}</ul>;
}

export default function App() {
  return (
    <QueryClientProvider client={browserClient}>
      <HydrationBoundary state={holat}>
        <h2>Loyihalar</h2>
        <List k="loyihalar" />
        <h2>Postlar</h2>
        <List k="postlar" />
      </HydrationBoundary>
    </QueryClientProvider>
  );
}

Konsolda:

text
kalitlar: 2

Ekranda:

text
Loyihalar
Vazifalar
Bahor
Postlar
Server Components
Portfolio v2

Bitta dehydrate — bitta obyekt, ichida ikkala kalit. HydrationBoundary ikkalasini birdaniga keshga qo'ydi. Ikkita komponent o'rniga bitta List + k prop — takrorlanishsiz. useQuery<string[]> — queryFn bo'sh massiv qaytargani uchun turni aniq aytdik.

3-mashq (qiyin): har so'rovga o'z keshi

«Xavfli xato» bo'limidagi umumiyKesh misolini tuzating: sahifaChiz har chaqiruvda o'z Map ini yaratsin (xuddi getQueryClient serverda qilganidek). Natija: Malikaning keshida faqat uning kaliti. Keyin soraladi hisoblagichi qo'shing: bitta so'rov ichida bir xil kalit ikki marta so'ralsa, ikkinchisi keshdan olinsin (bitta so'rov ichida kesh — foydali).

Yechim
js
let soraladi = 0;

function bazadanOl(foydalanuvchi) {
  soraladi++;
  return foydalanuvchi === "ali" ? ["osh"] : ["manti"];
}

function sahifaChiz(foydalanuvchi) {
  const kesh = new Map(); // har so'rov — yangi
  const ol = (k) => {
    if (!kesh.has(k)) kesh.set(k, bazadanOl(foydalanuvchi));
    return kesh.get(k);
  };
  ol(`buyurtmalar:${foydalanuvchi}`); // header uchun
  ol(`buyurtmalar:${foydalanuvchi}`); // ro'yxat uchun — keshdan
  return [...kesh.keys()];
}

console.log("Ali:", sahifaChiz("ali"));
console.log("Malika:", sahifaChiz("malika"));
console.log("bazaga so'rovlar:", soraladi);

Konsolda:

text
Ali: [ 'buyurtmalar:ali' ]
Malika: [ 'buyurtmalar:malika' ]
bazaga so'rovlar: 2

Ikki sahifa, har birida kalit ikki marta so'raldi — lekin bazaga faqat 2 so'rov (har foydalanuvchiga bittadan). Kesh so'rov ichida foydali, so'rovlar orasida xavfli. React cache() aynan shunday ishlaydi (React cache()), getQueryClient ning serverdagi tarmog'i ham.

4-mashq: Portfolio qadami — qaror yozing

Kanonda bu dars uchun holat yo'q: Query portfolio ga kirmadi. O'z loyihangizda bitta savolga javob yozing: "Qaysi sahifada Query kerak bo'lardi va nega hozir kerak emas?" Javob README'dagi "Server kodi qayerda" bo'limiga tushadi. Ixtiyoriy tajriba — alohida branch'da @tanstack/react-query ni o'rnatib, /loyihalar ni shu darsdagi naqsh bilan qayta yozing va brauzer yuklagan JS'ni DevTools Network panelida solishtiring. Branch'ni keyin o'chiring.

Yechim

Namuna javob: "Hozir hech qayerda. GitHub — serverda keshlanadi, forma — Server Action, filtr — URL. Kelajakda blogga izohlar qo'shilsa, ular tez-tez yangilanadi va cheksiz ro'yxat bo'ladi. Izohlar bloki uchun Query kerak: birinchi 10 ta izoh serverda prefetch, qolgani useInfiniteQuery." Tajribada JS taxminan 10 KB gzip oshishi kutiladi (19-qism o'lchovi); aniq raqamni o'zingiz o'lchang — kanonda bu o'lchov yo'q.

11. Real ishda

  • Dashboard va admin panellar Next'da ko'pincha shu naqsh bilan: jadvalning birinchi sahifasi serverda, filtrlar va sahifalash — Query.
  • tRPC + Next — Query'ning eng mashhur hamrohi: tRPC'ning React integratsiyasi Query ustida qurilgan va server prefetch uchun o'z yordamchilariga ega (GraphQL va tRPC).
  • SWR — Vercel'ning o'z kutubxonasi, g'oyasi bir xil (fallback bilan server ma'lumotini berish). RE-18 da solishtirgansiz.
  • Intervyu savoli: "Next App Router'da React Query kerakmi?" Kuchli javob — "ko'p hollarda yo'q". U Server Component, Server Action va kesh qaysi vazifalarni yopishini aytadi. Keyin Query qayerda qolishini (polling, cheksiz ro'yxat, optimistik UI) va staleTime hamda har so'rovga client tuzoqlarini qo'shadi.

Xulosa

  • Next'da ma'lumotning ko'p qismini Server Component await bilan oladi — Query kerak emas.
  • Query sahifa ochilgandan keyin brauzerda o'zgaradigan ma'lumot uchun: filtr, cheksiz ro'yxat, polling, optimistik UI.
  • Ulash naqshi: serverda prefetchQuery → dehydrate → <HydrationBoundary>; kalitlar bir xil, staleTime > 0 bo'lsin.
  • Serverda QueryClient har so'rovga yangi — aks holda foydalanuvchilar ma'lumoti aralashadi; brauzerda bitta.
  • portfolio ga Query kirmadi: hech bir ma'lumot brauzerda qayta so'ralmaydi, ~10 KB gzip foydasiz bo'lardi.

Keyingi dars: Metadata API — har sahifaga to'g'ri sarlavha, tavsif, canonical va Open Graph beramiz va openGraph ning kutilmagan "almashtirish" tuzog'ini ko'ramiz.

Manbalar

  • TanStack Query v5 hujjati: "Advanced Server Rendering", "Server Rendering & Hydration" (dehydrate, HydrationBoundary, isServer, staleTime) — tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr (2026-10-21 da o'qildi)
  • Next.js hujjati (16.4.0): "Backend for Frontend" — Caveats, Server Components (client'da ma'lumot olish holatlari)
  • Kanon qarori: holatsiz dars (Query portfolio ga kirmadi); Query hajmi — 19-qism o'lchovi (+9.9 KB gzip), yakuniy JS — natijalar/tekshiruv-43.txt
  • Jonli bloklar: @tanstack/react-query 5.104.1 (sayt natija oynasi), React 19.3.0
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
TanStack Query va Next.js: prefetchQuery, dehydrate va HydrationBoundary — IlmHamroh