Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Oldindan yuklash: prefetchQuery
- 2.1 Keshni oldindan to'ldirish
- 2.2 Qachon prefetch qilmaslik kerak
- 3. Router loader va kesh
- 3.1 ensureQueryData — loader'da
- 3.2 ensureQueryData va fetchQuery
- 4. useSuspenseQuery
- 4.1 Kutish — Suspense'ga
- 4.2 Sharshara tuzog'i
- 5. Bekor qilish va qayta urinish
- 5.1 signal — qachon bekor qilinadi
- 5.2 retry oraliqlari
- 6. DevTools va keshni saqlash
- 6.1 React Query DevTools
- 6.2 Keshni brauzerda saqlash (persist)
- 7. SWR — yengilroq muqobil
- 7.1 SWR nima
- 7.2 O'lchov: hajm
- 7.3 Qachon qaysi
- 8. Tajriba: vazifalar da prefetch va Suspense
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qaysi metod?
- 2-mashq (o'rta): useQuery dan useSuspenseQuery ga
- 3-mashq (qiyin): loader + kesh
- 11. Real ishda
- Xulosa
- Manbalar
TanStack Query prefetch va Suspense: oldindan yuklash, bekor qilish va SWR
Qisqacha: Ma'lumotni foydalanuvchi so'rashidan oldin olib qo'ysa bo'ladi:
prefetchQuery(sichqoncha havola ustiga kelganda) yoki router loader'idagiensureQueryData(sahifa chizilishidan oldin).useSuspenseQuerykutishni<Suspense>ga topshiradi — komponentdadatadoim bor.queryFnga berilgansignalkeraksiz so'rovni bekor qiladi. SWR — xuddi shu g'oyaning yengilroq (+5.4 KB), lekin imkoniyati kamroq muqobili.
Bu darsda
prefetchQuerybilan havola ustiga kelganda ma'lumotni oldindan yuklaysiz.- React Router loader'ida
ensureQueryDatavafetchQueryfarqini bilasiz. useSuspenseQuerybilan<Suspense>fallback'ini ishlatasiz vauseQuerydan farqini ko'rasiz.signalbilan so'rov qachon bekor qilinishini konsolda kuzatasiz;retryoraliqlarini hisoblaysiz.- DevTools va kesh saqlash (persist) nima berishini, SWR qachon yetarli ekanini bilasiz.
Oldin bilishingiz kerak: TanStack Query IV: sahifalash, TanStack Query II: kesh hayot sikli, Suspense, React Router IV: data mode, AbortController.
1. Nega bu kerak?
Hozirgacha so'rov foydalanuvchi sahifani ochgandan keyin ketardi. Mehmon «Osh» ni bosadi → sahifa ochiladi → "Yuklanmoqda…" → 300 ms → taom. Bu 300 ms ni yo'qotish mumkinmi?
Mumkin, chunki foydalanuvchi bosishdan oldin "ogohlantiradi". Sichqoncha havolaga yetib kelishi va bosish orasida o'rtacha 100–300 ms o'tadi. Telefonda barmoq tegishi va qo'yib yuborish orasida ham vaqt bor. Shu oraliqda so'rovni boshlasak, bosilganda ma'lumot tayyor yoki yarim yo'lda bo'ladi.
O'xshatish: tajribali ofitsiant. Mehmon menyuni varaqlab, osh sahifasida to'xtadi. Ofitsiant oshxonaga "osh tayyorlab turinglar" deb aytib qo'yadi. Mehmon buyurtma berganda — osh tayyor. Aytmagan bo'lsa ham hech narsa yo'qotilmaydi: osh boshqa mehmonga ketadi (kesh).
Uchinchi muammo — teskarisi: keraksiz so'rov. Mehmon «Osh» ni bosdi, fikrini o'zgartirib «Manti» ni bosdi. Osh so'rovi hamon yo'lda — trafik va server vaqti. Uni bekor qilish kerak.
2. Oldindan yuklash: prefetchQuery
2.1 Keshni oldindan to'ldirish
queryClient.prefetchQuery(sozlama) — useQuery kabi so'raydi va keshga yozadi, lekin hech qanday komponent unga obuna emas. Natijani qaytarmaydi, xato ham tashlamaydi — faqat keshni to'ldiradi. Kesh yangi bo'lsa (staleTime ichida) — hech narsa qilmaydi.
Haqiqiy loyihada prefetch havolaning onMouseEnter va onFocus hodisalarida chaqiriladi. Natija oynasining tekshiruvchisi sichqonchani "olib bora" olmaydi, shuning uchun bu misolda hover o'rniga alohida tugma:
import { useState } from "react";
import {
QueryClient,
QueryClientProvider,
queryOptions,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 60_000 } },
});
function fetchDish(slug) {
console.log("so'rov:", slug);
return new Promise((resolve) =>
setTimeout(() => resolve({ slug, price: 35000 }), 400),
);
}
const dishQuery = (slug) =>
queryOptions({
queryKey: ["dish", slug],
queryFn: () => fetchDish(slug),
});
function Dish({ slug }) {
const { isPending, data } = useQuery(dishQuery(slug));
if (isPending) return <p>Yuklanmoqda…</p>;
return <p>{data.slug}: {data.price} so'm</p>;
}
function Menu() {
const client = useQueryClient();
const [open, setOpen] = useState(null);
const prefetch = () => client.prefetchQuery(dishQuery("osh"));
return (
<>
<button onClick={prefetch}>Ustiga keldi (hover)</button>{" "}
<button onMouseEnter={prefetch} onClick={() => setOpen("osh")}>
Osh
</button>
{open && <Dish slug={open} />}
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Menu />
</QueryClientProvider>
);
}Konsolda:
so'rov: oshBu misolda soxta server sekinroq — 400 ms. Ekranda:
Ustiga keldi (hover) Osh
osh: 35000 so'm«Osh» bosilganda so'rov yo'q va "Yuklanmoqda…" ham yo'q. Oraliq nuqta narxni bosishdan bir zum keyin tekshiradi — 400 ms lik server javobidan ancha oldin u ekranda. U allaqachon keshda edi. «Ishga tushir» dan keyin o'zingiz sinab ko'ring: birinchi tugmani bosmasdan, sichqonchani «Osh» ustiga olib boring — onMouseEnter xuddi shu ishni qiladi.
Uch qoida:
- Kalit va funksiya — o'sha. Prefetch boshqa kalit bilan qilinsa, kesh to'ladi, lekin sahifa uni topmaydi.
queryOptionsfunksiyasi (TanStack Query II) bu xatoni yo'qotadi: ikkala joydishQuery("osh")ni oladi. staleTimekerak. Sukutdagi 0 da prefetch qilingan ma'lumot darhol eskiradi va sahifa ochilganda yana so'raladi (keshdan ko'rsatib, fonda). Ikki so'rov — bittasi ortiqcha.- Klaviatura ham.
onFocusni unutmang — Bobur sichqonchasiz, Tab bilan yuradi.
2.2 Qachon prefetch qilmaslik kerak
Prefetch — "ehtimol kerak bo'ladi" degan taxmin. Noto'g'ri taxmin — isrof:
- ro'yxatdagi 50 ta havolaning har biri ustidan sichqoncha o'tib ketadi — 50 ta so'rov;
- telefonda trafik pulli, internet sekin;
- ma'lumot og'ir (rasm, katta jadval) yoki server qimmat hisoblaydi.
Oddiy yo'l: hover'da kichik kechikish (100 ms ichida sichqoncha ketib qolsa — so'ramaslik) yoki faqat eng ehtimolli havolalarda.
3. Router loader va kesh
3.1 ensureQueryData — loader'da
React Router IV: data mode darsida loader sahifa chizilishidan oldin ma'lumot olishini ko'rdik. Loader ham bir xil "oldindan yuklash": havola bosildi → loader ishladi → sahifa ma'lumot bilan chizildi.
Loader va Query birga ishlaganda savol tug'iladi: ma'lumot qayerda saqlanadi? Javob — Query keshida. Loader keshni to'ldiradi, komponent keshdan o'qiydi. ensureQueryData — "keshda bo'lsa — uni ber, bo'lmasa — so'ra va keshga yoz":
import {
createMemoryRouter,
Link,
RouterProvider,
useParams,
} from "react-router";
import {
QueryClient,
QueryClientProvider,
queryOptions,
useSuspenseQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchDish(slug) {
console.log("so'rov:", slug);
return new Promise((resolve) =>
setTimeout(() => resolve({ name: slug, price: 35000 }), 200),
);
}
const dishQuery = (slug) =>
queryOptions({
queryKey: ["dish", slug],
queryFn: () => fetchDish(slug),
});
function Dish() {
const { slug } = useParams();
const { data } = useSuspenseQuery(dishQuery(slug));
return (
<>
<p>
{data.name}: {data.price} so'm
</p>
<Link to="/">Menyu</Link>
</>
);
}
const router = createMemoryRouter([
{ path: "/", element: <Link to="/taom/osh">Osh</Link> },
{
path: "/taom/:slug",
element: <Dish />,
loader: ({ params }) =>
queryClient.ensureQueryData(dishQuery(params.slug)),
},
]);
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
);
}Konsolda:
so'rov: oshEkranda:
osh: 35000 so'm
MenyuIkki marta osh sahifasiga kirdik, so'rov — bitta. Birinchi marta loader so'radi va router javobni kutib, keyin sahifani chizdi. Ikkinchi marta ensureQueryData keshda ma'lumotni topdi va darhol qaytardi.
Komponentda useSuspenseQuery — keyingi bo'limning mavzusi. Hozircha: loader keshni to'ldirgan, shuning uchun komponent hech qachon kutmaydi va data doim bor.
Bu misolda ham manzil satri yo'q natija oynasi uchun xotiradagi router (createMemoryRouter) ishlatildi. Loyihada — createBrowserRouter, qolgan kod bir xil.
3.2 ensureQueryData va fetchQuery
Loader uchun ikki metod bor. Farqi — eskirgan ma'lumotda:
| Metod | Keshda yangi | Keshda eskirgan | Keshda yo'q |
|---|---|---|---|
ensureQueryData |
beradi | beradi (so'ramaydi) | so'raydi, kutadi |
fetchQuery |
beradi | so'raydi, kutadi | so'raydi, kutadi |
prefetchQuery |
— | so'raydi | so'raydi |
ensureQueryData — eng tez sahifa o'tishi: keshda nima bo'lsa, shu. Eskirgan bo'lsa ham. Komponentdagi useQuery/useSuspenseQuery keyin uni fonda yangilaydi. revalidateIfStale: true bilan ensureQueryData o'zi ham fonda yangilashni boshlaydi.
fetchQuery — eskirgan bo'lsa yangisini kutadi. vazifalar dagi vazifa sahifasi loader'i shuni ishlatadi (TanStack Query II «Vazifalar qadami»da ko'rdik): vazifa boshqa qurilmada o'chirilgan bo'lishi mumkin. Loader 404 ni to'g'ri qaytarishi uchun 30 soniyadan eski ma'lumotga ishonmaydi.
4. useSuspenseQuery
4.1 Kutish — Suspense'ga
Suspense darsida <Suspense fallback> ichidagi komponent "to'xtab" turishi va React fallback'ni ko'rsatishini o'rgandik. useSuspenseQuery — shu mexanizmni Query bilan ulaydi. Ma'lumot keshda yo'q bo'lsa, komponent to'xtaydi (ichida Promise tashlanadi) va eng yaqin <Suspense> fallback'i chiqadi:
import { Suspense } from "react";
import {
QueryClient,
QueryClientProvider,
useSuspenseQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchMenu() {
console.log("so'rov ketdi");
return new Promise((resolve) =>
setTimeout(() => resolve(["Osh", "Manti"]), 200),
);
}
function Menu() {
const { data } = useSuspenseQuery({
queryKey: ["menu"],
queryFn: fetchMenu,
});
// isPending tekshiruvi yo'q — data doim bor
return <p>{data.join(", ")}</p>;
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<h3>«Bahor» menyusi</h3>
<Suspense fallback={<p>Yuklanmoqda…</p>}>
<Menu />
</Suspense>
</QueryClientProvider>
);
}Konsolda:
so'rov ketdiEkranda:
«Bahor» menyusi
Osh, MantiuseQuery dan farqlar:
useQuery |
useSuspenseQuery |
|
|---|---|---|
data turi |
T | undefined |
T — doim bor |
| kutish | isPending — komponent o'zi |
<Suspense fallback> |
| xato | isError — komponent o'zi |
eng yaqin Error Boundary'ga tashlanadi |
enabled, placeholderData |
bor | yo'q |
Kutish ham, xato ham komponentdan chiqib ketadi. Komponent faqat "baxtli yo'l" ni yozadi. Xatoni Error boundary ushlaydi. «Qayta urinish» uchun Query'da QueryErrorResetBoundary (yoki useQueryErrorResetBoundary) bor: boundary qayta tiklanganda xato bergan so'rovlar ham qaytadan ketadi.
enabled nega yo'q? useSuspenseQuery va'dasi — "data doim bor". O'chirilgan so'rovda ma'lumot bo'lmasdi. Bog'liq so'rovlarni Suspense bilan ketma-ket komponentlarga bo'lib yoziladi: biri to'xtaydi, keyin ichkisi.
4.2 Sharshara tuzog'i
Bitta komponentda ikkita useSuspenseQuery — ular parallel emas. Birinchisi to'xtaydi, komponent qolgan kodni bajarmaydi, ikkinchi so'rov faqat birinchisi tugagach boshlanadi. Bu TanStack Query II darsidagi sharshara — faqat endi ko'zga ko'rinmaydi.
Davosi:
- ikkalasini bitta
useSuspenseQueriesga yig'ish (useQueriesning Suspense ko'rinishi); - yoki loader'da ikkalasini oldindan parallel boshlash:
Promise.all([ensureQueryData(a), ensureQueryData(b)]).
Tekshirib ko'ring: Loader
ensureQueryDatabilan keshni to'ldirgan. Sahifa komponentiuseSuspenseQueryishlatadi. Fallback qachon ko'rinadi?
Javob
Odatda hech qachon. Router sahifani loader tugagach chizadi — ma'lumot allaqachon keshda, komponent to'xtamaydi. Fallback faqat kesh shu orada tozalangan bo'lsa (gcTime o'tgan, removeQueries chaqirilgan) ko'rinadi. Shuning uchun loader + useSuspenseQuery — mashhur juftlik: loader tezlik beradi, useSuspenseQuery esa data turini toza qiladi.
5. Bekor qilish va qayta urinish
5.1 signal — qachon bekor qilinadi
TanStack Query I da queryFn kontekstida signal borligini aytgan edik. Query uni qachon bekor qiladi? Ikki holatda:
- so'rovni hech kim kutmay qoldi — komponent yopildi yoki kalit o'zgardi va shu kalitga boshqa kuzatuvchi yo'q;
- siz o'zingiz
cancelQueriesni chaqirdingiz.
Muhim shart: Query faqat signal ishlatilgan bo'lsa bekor qiladi. queryFn uni o'qimasa, Query "bu funksiya bekor qilishni bilmaydi" deb hisoblaydi va so'rovni oxirigacha kutadi (natijani keshga ham yozadi).
Mehmon tez-tez taom almashtiradi:
import { useState } from "react";
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchDish(slug, signal) {
console.log("boshlandi:", slug);
signal.addEventListener("abort", () =>
console.log("bekor qilindi:", slug),
);
return new Promise((resolve) =>
setTimeout(() => resolve(`${slug} tayyor`), 500),
);
}
function Dish({ slug }) {
const { data } = useQuery({
queryKey: ["dish", slug],
queryFn: ({ signal }) => fetchDish(slug, signal),
});
return <p>{data ?? "Yuklanmoqda…"}</p>;
}
function Picker() {
const [slug, setSlug] = useState("osh");
return (
<>
<button onClick={() => setSlug("osh")}>Osh</button>{" "}
<button onClick={() => setSlug("manti")}>Manti</button>
<Dish slug={slug} />
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Picker />
</QueryClientProvider>
);
}Konsolda:
boshlandi: osh
boshlandi: manti
bekor qilindi: manti
boshlandi: osh
bekor qilindi: osh
boshlandi: mantiEkranda:
Osh Manti
manti tayyorQatorma-qator:
| Qadam | Voqea |
|---|---|
| ochilish | osh so'raldi va 500 ms da keldi (server ataylab sekin — bosishlar javobdan oldin ulgursin) |
| «Manti» | manti so'raldi |
| 100 ms dan keyin «Osh» | manti hali yo'lda va endi hech kim kutmaydi → bekor. Osh keshda bor, lekin eskirgan → fonda yangilash |
| 50 ms dan keyin «Manti» | osh yangilanishi bekor; manti — yangidan |
Bu soxta serverda signal faqat konsolga yozadi. Haqiqiy kodda uni fetch ga berasiz va brauzer so'rovni haqiqatan to'xtatadi: fetch(url, { signal }). DevTools Network panelida bunday so'rov "(canceled)" bo'lib ko'rinadi.
Bekor qilingan so'rov xato emas. Query uni isError ga aylantirmaydi: kesh oldingi holatiga qaytadi. Shuning uchun vazifalar dagi royxatniYukla signal.aborted bo'lsa konsolga "Sinxron bo'lmadi" yozmaydi (TanStack Query I «Vazifalar qadami»).
5.2 retry oraliqlari
TanStack Query I da sukutdagi 3 qayta urinish va 1 s, 2 s, 4 s oraliqlarini aytdik. Bu oraliqlar formula bilan hisoblanadi — har safar ikki baravar, lekin 30 soniyadan oshmaydi. Bu eksponensial kutish (exponential backoff): server og'ir ahvolda bo'lsa, minglab mijoz unga bir vaqtda qayta-qayta urilmaydi.
// TanStack Query'ning sukutdagi retryDelay formulasi
const retryDelay = (attempt) =>
Math.min(1000 * 2 ** attempt, 30000);
for (let attempt = 0; attempt < 7; attempt++) {
const ms = retryDelay(attempt);
console.log(`${attempt + 1}-qayta urinishdan oldin: ${ms} ms`);
}Konsolda:
1-qayta urinishdan oldin: 1000 ms
2-qayta urinishdan oldin: 2000 ms
3-qayta urinishdan oldin: 4000 ms
4-qayta urinishdan oldin: 8000 ms
5-qayta urinishdan oldin: 16000 ms
6-qayta urinishdan oldin: 30000 ms
7-qayta urinishdan oldin: 30000 msOltinchidan boshlab 30 s chegarasi ishladi (2 ** 5 * 1000 = 32000 → 30 000). retryDelay ni o'zgartirish mumkin — misollarda natija oynasini kutdirmaslik uchun 200 ms qildik. Internet yo'q paytda urinishlar sanalmaydi: so'rov paused bo'lib turadi (TanStack Query I).
6. DevTools va keshni saqlash
6.1 React Query DevTools
Kesh ko'rinmas narsa. Qaysi kalitlar bor, qaysi biri eskirgan, qachon yangilanadi — konsolga log yozmasdan bilish qiyin. React Query DevTools — sahifa burchagidagi panel: har kalit, uning holati (fresh, stale, fetching, paused, inactive), ichidagi ma'lumot. Undan qo'lda invalidatsiya qilish, xato yoki yuklanishni sun'iy chaqirish ham mumkin.
// o'z loyihangizda: npm i -D @tanstack/react-query-devtools
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>;Paket faqat dev rejimida ishlaydi: production build'da ReactQueryDevtools bo'sh komponentga aylanadi va to'plamga kirmaydi. Natija oynasida bu paket yo'q — shuning uchun misol tekshirilmagan.
TanStack Query II dagi staleTime tanlovi DevTools'da ko'zga ko'rinadi: kalit yashil (fresh) dan sariqqa (stale) qachon o'tishini kuzatasiz.
6.2 Keshni brauzerda saqlash (persist)
Sahifa yangilansa, Query keshi yo'qoladi — u xotirada. Persist — keshni localStorage yoki IndexedDB ga yozib, keyingi ochilishda tiklash. Query'da buning uchun alohida paketlar bor:
// o'z loyihangizda: @tanstack/react-query-persist-client va
// @tanstack/query-sync-storage-persister
import {
PersistQueryClientProvider,
} from "@tanstack/react-query-persist-client";
import {
createSyncStoragePersister,
} from "@tanstack/query-sync-storage-persister";
const queryClient = new QueryClient({
defaultOptions: { queries: { gcTime: 1000 * 60 * 60 * 24 } },
});
const persister = createSyncStoragePersister({
storage: window.localStorage,
});
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
>
<App />
</PersistQueryClientProvider>;Ikki nozik joy: gcTime saqlash muddatidan (sukut — 24 soat) kichik bo'lmasin, aks holda tiklangan yozuv darhol o'chadi. Va tiklash tugaguncha so'rovlar kutib turadi.
vazifalar bu paketlarni ishlatmaydi. Unda butun kesh emas, bitta ro'yxat kerak — u 13-qismdan beri localStorage da. Uni initialData qilib berish (TanStack Query II) — sinxron, birinchi kadrdayoq, qo'shimcha paketsiz.
7. SWR — yengilroq muqobil
7.1 SWR nima
SWR — Vercel (Next.js mualliflari) yozgan kichik kutubxona. Nomi HTTP keshlash strategiyasidan: stale-while-revalidate — "eskisini ko'rsat, shu payt yangisini tekshir". Bu strategiyani Kesh strategiyalari va offline ishlash da ham ko'rgansiz. TanStack Query ham xuddi shunday ishlaydi — farq imkoniyatlar soni va hajmda.
Tajribada (tajriba/variantlar/server-swr.tsx, kanondan aynan) o'sha vazifa — ro'yxatni yuklash va qo'shish — SWR bilan:
// server-swr.tsx — SWR 2 (stale-while-revalidate): useSWR + mutate
import useSWR, { useSWRConfig } from "swr";
import { ishgaTushir } from "./_umumiy.tsx";
import { Korinish, qosh, royxatniOl } from "./_server.tsx";
function Ilova() {
const { mutate } = useSWRConfig();
const { data, error, isLoading } = useSWR("vazifalar", () => royxatniOl());
return (
<Korinish
vazifalar={data?.vazifalar}
yuklanmoqda={isLoading}
xato={error instanceof Error ? error.message : null}
onQosh={(matn) => void qosh(matn).then(() => mutate("vazifalar"))}
/>
);
}
ishgaTushir(<Ilova />);Tanish narsalar: useSWR(kalit, funksiya) — useQuery ning o'zi, kalit — satr (yoki massiv). mutate("vazifalar") — invalidateQueries ning o'xshashi. Provider shart emas — SWR'da global kesh bor. Yozish uchun alohida hook ham bor (useSWRMutation), lekin bu tajribada oddiy mutate yetdi.
Xuddi shu vazifaning TanStack Query versiyasi (server-query.tsx, aynan):
function Ilova() {
const mijoz = useQueryClient();
const sorov = useQuery({ queryKey: ["vazifalar"], queryFn: ({ signal }) => royxatniOl(signal) });
const yozuv = useMutation({ mutationFn: qosh, onSettled: () => mijoz.invalidateQueries({ queryKey: ["vazifalar"] }) });
return (
<Korinish
vazifalar={sorov.data?.vazifalar}
yuklanmoqda={sorov.isPending}
xato={sorov.error?.message ?? yozuv.error?.message ?? null}
onQosh={(matn) => yozuv.mutate(matn)}
/>
);
}7.2 O'lchov: hajm
To'rtta variant — qo'lda (useEffect + fetch), SWR, TanStack Query va RTK Query — bitta vazifani bajaradi. To'rtalasi Chrome 155 da haqiqiy «Bahor» serveri bilan ishladi: GET → POST → GET (natijalar/tajriba/16-17-server-ishlaydi.txt). Build hajmi (gzip, React bilan birga; natijalar/tajriba/hajm.txt): qo'lda — 68 001 B, SWR — 73 381, Query — 78 333, RTK Query — 92 813. Qo'lda yozilganga nisbatan farq:
- qo'lda (useEffect)0 KB
- SWR 2.5.15,4 KB
- TanStack Query 5.104.110,3 KB
- RTK Query 2.13.0Redux bilan birga24,8 KB
Manba: sinov/19/natijalar/tajriba/hajm.txt (vite build, gzip -9, 2026-10-13)
vazifalar ning o'zida Query'ning narxi biroz kamroq chiqdi — +9.9 KB: ilova kodining bir qismi (qo'lda yozilgan "xo'jalik") o'chirildi.
7.3 Qachon qaysi
| SWR | TanStack Query | |
|---|---|---|
| hajm | ~5 KB | ~10 KB |
| mutatsiya | mutate, useSWRMutation |
useMutation, callback'lar, isMutating |
| optimistik | optimisticData, rollbackOnError |
onMutate / variables |
| sahifalash | useSWRInfinite |
useInfiniteQuery, maxPages |
oflayn, paused |
yo'q | bor (networkMode) |
| DevTools | rasmiy yo'q | bor |
| router loader | qo'lda | ensureQueryData, queryOptions |
SWR'ni kamsitmang. U to'g'ri tanlov bo'ladi, agar:
- ilova asosan o'qiydi (blog, katalog, dashboard), yozuv kam;
- Next.js loyihasi va jamoa Vercel ekotizimida;
- har kilobayt muhim (sekin internetli mobil auditoriya).
vazifalar uchun Query tanlandi: yozuv ko'p va bir vaqtda (optimistik, isMutating), oflayn rejim (13-qismdan beri — PWA), router loader bilan bitta kesh. Bular SWR'da qo'lda yoziladigan narsalar. RTK Query — loyihada Redux allaqachon bo'lsa (RTK Query darsida).
8. Tajriba: vazifalar da prefetch va Suspense
Bu darsda vazifalar kodi o'zgarmaydi. Har mavzu bo'yicha kanon nima qildi va nega:
- Ma'lumotni prefetch — kerak emas. Ro'yxat bitta kalitda va hamma sahifa uni ko'radi. TanStack Query II o'lchovi: navigatsiyada 0 so'rov — ma'lumot allaqachon keshda.
- Kodni prefetch — statistika va sozlamalar sahifalarini
route.lazybilan alohida faylga ajratish sinaldi (natijalar/17-lazy-marshrut.txt). Foyda chiqmadi: sahifalar kodi kichik (0.6 va 0.3 KB gzip). Bundler (Rolldown) umumiy modullarni alohida "vazifa" bo'lagiga ajratdi — ro'yxat sahifasi uni ham yuklaydi. Natija: 130.5 KB va +1 so'rov, lazy'siz — 129.8 KB. Kirmadi. useSuspenseQuery— ro'yxat doim keshda (initialData— localStorage), so'rov hech qachon to'xtatmaydi. Suspense chegarasi hech narsa bermasdi. Suspense darsidagi qaror kuchida.- Bekor qilish — bor:
queryFn({ signal })→api.royxat({ signal })→fetch. VacancelQueriesikki joyda: yozuv boshlanganda (TanStack Query III) va internet uzilganda (offlinehodisasi). - SWR va RTK Query — yuqoridagi o'lchov. Kanonga kirmadi.
# 19/#17: statistika va sozlamalar sahifalari lazy marshrut (route.lazy + import()) — o'lchov, KANONGA KIRMADI
# Xulosa: sahifalar kodi kichik (0.6 + 0.3 KB gzip); Rolldown umumiy modullarni (vazifa.ts...) alohida 'vazifa' chunk'iga ajratdi — ro'yxat sahifasi uni ham yuklaydi: index 127.0 + vazifa 3.5 = 130.5 KB (lazy'siz index ~129.8 KB) va +1 so'rov. Foyda yo'q — kirmadi.Qoida: optimallashtirishni o'lchovsiz qilmang. "Lazy — tezroq" degan umumiy gap bu yerda noto'g'ri chiqdi.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| prefetch qilindi, sahifa baribir so'radi | staleTime: 0 |
staleTime ni oshiring |
| prefetch keshini sahifa topmadi | kalit boshqa | bitta queryOptions funksiyasi |
| bitta komponentda ikki Suspense so'rov sekin | ketma-ket to'xtaydi (sharshara) | useSuspenseQueries yoki loader'da parallel |
useSuspenseQuery da enabled ishlamaydi |
bu sozlama yo'q | komponentlarga bo'ling yoki useQuery |
| xato oq ekran berdi | useSuspenseQuery xatosini ushlovchi yo'q |
Error Boundary + QueryErrorResetBoundary |
| tez almashtirishda eski so'rovlar tugaydi | signal ishlatilmagan |
fetch(url, { signal }) |
| oynaga qaytganda tiklangan kesh o'chdi | persist'da gcTime < maxAge |
gcTime ni oshiring |
10. Mashqlar
Mashqlar kurs/mashqlar/19/17-prefetch/ da.
1-mashq (oson): qaysi metod?
Loader uchun metod tanlang: ensureQueryData yoki fetchQuery.
- Taom sahifasi: tavsif kuniga bir marta o'zgaradi, sahifa tez ochilsin →
- Bank o'tkazmasi sahifasi: balans doim eng yangi bo'lsin →
vazifalardagi vazifa sahifasi: o'chirilgan vazifa uchun 404 to'g'ri bo'lsin →
Yechim
ensureQueryData eskirgan keshni ham darhol beradi — tez, lekin eski bo'lishi mumkin. Taom tavsifi uchun bu yaxshi. fetchQuery eskirganini serverdan yangilab, keyin beradi — balans va "bormi-yo'qmi" savoli uchun shu kerak.
2-mashq (o'rta): useQuery dan useSuspenseQuery ga
Komponentni useSuspenseQuery ga o'tkazing: isPending tekshiruvi ketsin, kutish <Suspense fallback={<p>Yuklanmoqda…</p>}> ga o'tsin.
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchTables() {
return new Promise((resolve) =>
setTimeout(() => resolve([2, 4, 7]), 200),
);
}
function FreeTables() {
const { isPending, data } = useQuery({
queryKey: ["tables"],
queryFn: fetchTables,
});
if (isPending) return <p>Yuklanmoqda…</p>;
return <p>Bo'sh stollar: {data.join(", ")}</p>;
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<FreeTables />
</QueryClientProvider>
);
}Ekranda:
Bo'sh stollar: 2, 4, 7Yechim
import { Suspense } from "react";
import {
QueryClient,
QueryClientProvider,
useSuspenseQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchTables() {
return new Promise((resolve) =>
setTimeout(() => resolve([2, 4, 7]), 200),
);
}
function FreeTables() {
const { data } = useSuspenseQuery({
queryKey: ["tables"],
queryFn: fetchTables,
});
return <p>Bo'sh stollar: {data.join(", ")}</p>;
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Suspense fallback={<p>Yuklanmoqda…</p>}>
<FreeTables />
</Suspense>
</QueryClientProvider>
);
}Ekranda:
Bo'sh stollar: 2, 4, 7Komponent bir qatorga qisqardi: faqat ma'lumot bor holat. "Yuklanmoqda…" endi App da — bir nechta komponent bitta fallback ostida tursa, ular birga chiqadi.
3-mashq (qiyin): loader + kesh
«Bahor» stol sahifasi: /stol/:id. Yozing:
tableQuery(id)—queryOptions, kalit["table", id],fetchTable(id)dan.- Marshrut loader'i —
ensureQueryData(tableQuery(params.id)). Tablekomponenti —useSuspenseQuery(tableQuery(id)), ekranda "4-stol: 2 mehmon" va «Orqaga» havolasi (/).- Bosh sahifa (
/) — «4-stol» havolasi. - 4-stolga ikki marta kirilganda serverga bitta so'rov.
Server:
const GUESTS = { 4: 2, 7: 5 };
function fetchTable(id) {
console.log("so'rov: stol", id);
return new Promise((resolve) =>
setTimeout(() => resolve({ id, guests: GUESTS[id] }), 200),
);
}Ishora: «ensureQueryData — loader'da» bo'limidagi misol. params.id — satr (React Router II dagi qoida), GUESTS["4"] ham ishlaydi.
Yechim
import {
createMemoryRouter,
Link,
RouterProvider,
useParams,
} from "react-router";
import {
QueryClient,
QueryClientProvider,
queryOptions,
useSuspenseQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
const GUESTS = { 4: 2, 7: 5 };
function fetchTable(id) {
console.log("so'rov: stol", id);
return new Promise((resolve) =>
setTimeout(() => resolve({ id, guests: GUESTS[id] }), 200),
);
}
const tableQuery = (id) =>
queryOptions({
queryKey: ["table", id],
queryFn: () => fetchTable(id),
});
function Table() {
const { id } = useParams();
const { data } = useSuspenseQuery(tableQuery(id));
return (
<>
<p>
{data.id}-stol: {data.guests} mehmon
</p>
<Link to="/">Orqaga</Link>
</>
);
}
const router = createMemoryRouter([
{ path: "/", element: <Link to="/stol/4">4-stol</Link> },
{
path: "/stol/:id",
element: <Table />,
loader: ({ params }) =>
queryClient.ensureQueryData(tableQuery(params.id)),
},
]);
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
);
}Konsolda:
so'rov: stol 4Ekranda:
4-stol: 2 mehmon
OrqagaIkkinchi kirishda ensureQueryData keshdan oldi. tableQuery loader va komponentda bir xil — kalit adashmaydi. Kalitda id satr ("4"): manzildan kelgani shunday. Agar bir joyda son, boshqa joyda satr bersangiz (["table", 4] va ["table", "4"]), bu ikki xil kalit bo'ladi.
11. Real ishda
- Hover prefetch — katta saytlarda odatiy: GitHub, Vercel, ko'p internet-do'konlar. Next.js
<Link>ko'rinadigan havolalarning kodini o'zi oldindan yuklaydi (Next.js qismida). Ma'lumotni esa siz prefetch qilasiz. - Router + Query — TanStack Router va React Router data mode'da loader ichida
ensureQueryData— tavsiya etilgan naqsh. Loader tezlikni, Query keshni beradi. - Suspense — React'ning yangi API'lari (
use, server komponentlar) Suspense atrofida qurilgan.useSuspenseQueryshu yo'nalishga mos. - DevTools — "nega bu so'rov ikki marta ketdi?" savoliga eng tez javob. Ishga kirganda birinchi o'rnatadigan narsalardan biri.
- Intervyu savollari: "prefetch qanday ishlaydi va qachon zararli?", "
useSuspenseQueryvauseQueryfarqi?", "Query so'rovni qachon bekor qiladi?", "SWR va React Query farqi?".
Xulosa
prefetchQuery— keshni oldindan to'ldiradi (hover, focus). Bir xilqueryOptionsva yetarlistaleTimebilan.- Loader'da:
ensureQueryData— keshdagini darhol (eskirgan bo'lsa ham),fetchQuery— eskirganini yangilab. useSuspenseQuery—datadoim bor, kutish<Suspense>ga, xato Error Boundary'ga. Bitta komponentda ikkitasi — sharshara.- So'rov hech kim kutmay qolganda yoki
cancelQueriesda bekor qilinadi — faqatsignalishlatilgan bo'lsa. Bekor — xato emas. retryoraliqlari: 1, 2, 4… s, 30 s gacha. DevTools — keshni ko'rish, persist — saqlash.- SWR (+5.4 KB) — o'qishga yo'naltirilgan ilovalar uchun yetarli; ko'p yozuv va oflaynda Query kuchliroq.
Keyingi dars: Real-time: WebSocket, SSE va Socket.IO frontendda — server o'zi xabar yuborganda React'ni qanday ulash; vazifalar da boshqa qurilmadagi o'zgarish 254 ms da.
Manbalar
- TanStack Query v5: Prefetching & Router Integration — tanstack.com/query/v5/docs/framework/react/guides/prefetching (2026-10, v5.104)
- TanStack Query v5: Suspense, Query Cancellation, Query Retries — …/guides/suspense, …/guides/query-cancellation, …/guides/query-retries
- TanStack Query v5: Devtools,
persistQueryClient— …/devtools, …/plugins/persistQueryClient - SWR: Getting Started, Mutation — swr.vercel.app/docs/getting-started (v2.5)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!