Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ikki dunyo: RSC va Query
- 2.1 Taqqoslash
- 2.2 Qaror savollari
- 3. Muammo: SSR'da useQuery
- 4. Naqsh: prefetch → dehydrate → HydrationBoundary
- 4.1 Uch qadam
- 4.2 Next'dagi kod
- 4.3 Natija oynasida: dehydrate va HydrationBoundary
- 4.4 Tuzoq: staleTime: 0
- 5. QueryClient: serverda har so'rovga yangi
- 5.1 Xavfli xato
- 5.2 To'g'ri naqsh: getQueryClient
- 6. Cheksiz ro'yxat va oqim
- 6.1 prefetchInfiniteQuery
- 6.2 Kutmasdan jo'natish (ko'rsatkich)
- 7. portfolio ga Query nega kirmadi
- 8. Qachon qaysi — yakuniy jadval
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): RSC yoki Query?
- 2-mashq (o'rta): ikki kalit
- 3-mashq (qiyin): har so'rovga o'z keshi
- 4-mashq: Portfolio qadami — qaror yozing
- 11. Real ishda
- Xulosa
- Manbalar
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
prefetchQuerybilan 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 (agarstaleTime> 0).
Bu darsda
- Server Component'dagi
awaitva client'dagiuseQueryqachon nimaga kerakligini jadval bilan ajrata olasiz. prefetchQuery→dehydrate→HydrationBoundaryzanjirini tushuntirasiz va natija oynasida jonli ko'rasiz.staleTime: 0nega sahifa ochilishi bilan ikkinchi so'rov yuborishini ko'rasiz.- Serverda nega har so'rovga yangi
QueryClientkerakligini (aks holda foydalanuvchilar ma'lumoti aralashadi) bilasiz. portfolioga 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:
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:
portfoliodagi 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?
- Server sahifani chizadi.
useQueryserverda ham ishlaydi, lekin u so'rov yubormaydi — HTML'gaisPendingholati, ya'ni «Yuklanmoqda…» tushadi. - Brauzer HTML'ni ko'rsatadi — skelet.
- JS yuklanadi, hydration bo'ladi, endi so'rov ketadi.
- 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):
// 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>
);
}// _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"]. ClientuseQuerykeshdan aynan shu kalit bo'yicha topadi. Kalit farq qilsa, prefetch behuda. queryFnlar 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'ningqueryFni 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:
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:
dehydrate: ["Vazifalar","Bahor"]Ekranda:
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):
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:
tarmoq so'roviEkranda:
Vazifalar
Bahor
LandingMa'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, clientuseQuery({ 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:
// ❌ 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:
// ❌ 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:
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:
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:
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
vazifalarni 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
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:
kalitlar: 2Ekranda:
Loyihalar
Vazifalar
Bahor
Postlar
Server Components
Portfolio v2Bitta 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
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:
Ali: [ 'buyurtmalar:ali' ]
Malika: [ 'buyurtmalar:malika' ]
bazaga so'rovlar: 2Ikki 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 (
fallbackbilan 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
staleTimehamda har so'rovga client tuzoqlarini qo'shadi.
Xulosa
- Next'da ma'lumotning ko'p qismini Server Component
awaitbilan 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
QueryClienthar so'rovga yangi — aks holda foydalanuvchilar ma'lumoti aralashadi; brauzerda bitta. portfolioga 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
portfolioga 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
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!