12.4-bob: TanStack Query (React Query)
12-QISM — State Management va Data Fetching · 4-mavzu
1. Kirish va motivatsiya
12.3-bobda RTK Query bilan server-state'ni boshqardik — kesh, invalidation, optimistic update. Lekin RTK Query'ning bir cheklovi bor: u Redux'ga bog'liq (RTK ekotizimida yashaydi). Agar loyihangizda Redux bo'lmasa (yoki siz Redux ishlatmoqchi bo'lmasangiz), RTK Query uchun Redux'ni qo'shishga majbur bo'lasiz. Aynan shu yerda TanStack Query (eski va keng tanilgan nomi — React Query) keladi: u server-state boshqaruvining mustaqil, universal yechimi — Redux yoki boshqa hech narsaga bog'liq emas, faqat React (va aslida framework'dan mustaqil — Vue, Solid, Svelte versiyalari ham bor). Va eng muhimi — bugun TanStack Query eng keng tavsiya etiladigan va eng ko'p ishlatiladigan server-state vositasi.
TanStack Query va RTK Query bir xil falsafaga asoslanadi (server state — client state'dan alohida — 12.1: 2.10, 12.3: 2.1), bir xil muammolarni (kesh, sync, loading, deduplication, fonda yangilash) hal qiladi. Farq — yondashuv va ekotizimda: RTK Query deklarativ (endpoint'larni oldindan ta'riflaysiz, hooklar avtomatik), TanStack Query imperativroq va moslashuvchanroq (useQueryda to'g'ridan query function berasiz — istalgan fetch/axios bilan). TanStack Query'ning kattaroq community'si, ko'proq funksiyasi (infinite queries, parallel/dependent queries, optimistic — ancha qulay), va ajoyib DevTools'i bor. Ko'p dasturchi uni "frontend'ning eng yaxshi kutubxonasi" deb ataydi — chunki u server bilan ishlashni tubdan soddalashtiradi.
Bu bob: TanStack Query nima (mustaqil server-state — RTK Query bilan taqqos), setup (QueryClient, QueryClientProvider), useQuery (queryKey, queryFn), query hayot sikli va keshlash (staleTime, gcTime), loading/error holatlari (isPending/isFetching/error), query keys (struktura, dependency), dependent va parallel queries, useMutation (server o'zgartirish), invalidation va cache update (kesh sinxronlash), optimistic updates, pagination va infinite queries, prefetching va DevTools, va RTK Query vs TanStack Query (yakuniy qaror). Uni to'liq — server-state falsafasi va amaliy holatda — ochamiz.
O'xshatish: TanStack Query — bu shaxsiy yordamchi (assistant) bilan kutubxona. Oddiy fetch (
useEffect+fetch — 11.5) — bu o'zingiz kutubxonaga borib, kitobni qidirib, olib kelib, o'qib, joyiga qaytarish (har gal, qo'lda, takror). TanStack Query — bu aqlli yordamchi: "menga falon kitob kerak" desangiz (useQuery), u kitobni keltiradi va eslab qoladi (kesh) — keyingi safar so'rasangiz darrov beradi. Kitob eskirsa (yangi nashr chiqsa — staleness), u fonda yangisini keltiradi (siz eskisini o'qib turgan paytda). Bir nechta odam bir kitobni so'rasa — u bir marta olib, hammaga beradi (deduplication). Siz kitobni o'zgartirsangiz (mutation — masalan izoh qo'shsangiz), u tegishli kitoblarning nusxalarini yangilaydi (invalidation). Siz faqat "nima kerak"ni aytasiz — qolganini aqlli yordamchi (TanStack Query) qiladi.
Nega muhim?
- Eng keng server-state vositasi — bugun React loyihalarining katta qismi TanStack Query ishlatadi (standart).
- Mustaqil, universal — Redux yoki boshqa hech narsa kerak emas (faqat o'zi — har loyihada).
- Zamonaviy stack'ning tarkibiy qismi — bugungi React ekotizimida server-state uchun birinchi tavsiya.
- Tub soddalashtirish —
useEffect+fetch'ning (loading/error/kesh/race) butun og'rig'ini yo'qotadi.
2. Nazariya — chuqur tushuntirish
2.1. TanStack Query nima (RTK Query bilan taqqos)
TANSTACK QUERY — MUSTAQIL server-state (data fetching + caching) kutubxonasi:
npm install @tanstack/react-query
(Redux KERAK EMAS — har loyihada, framework'dan mustaqil)
RTK QUERY vs TANSTACK QUERY (ikkalasi server state — farq):
┌──────────────────┬──────────────────────┬──────────────────────┐
│ │ RTK Query 12.3-bob │ TanStack Query (bu bob)│
├──────────────────┼──────────────────────┼──────────────────────┤
│ Bog'liqlik │ Redux KERAK │ MUSTAQIL (hech narsa) │
│ Yondashuv │ deklarativ (endpoint)│ imperativ (queryFn) │
│ Setup │ createApi (oldindan) │ useQuery (joyida) │
│ Moslashuvchanlik │ tartibli, Redux ichida│ ko'proq erkin/keng │
│ Community │ Redux ekotizimi │ ENG KATTA (standart) │
└──────────────────┴──────────────────────┴──────────────────────┘
Ikkalasi bir muammoni (server state) hal qiladi — farq ekotizim va uslubda
Redux loyiha RTK Query (tabiiy); aks holda TanStack Query (ko'pincha afzal)TanStack Query nima — mustaqil server-state kutubxonasi (
@tanstack/react-query). Eski nomi React Query edi (4-versiyadan "TanStack Query" — chunki Vue, Solid, Svelte versiyalari ham chiqdi — framework'dan mustaqil yadro). Eng muhim — u Redux'ga (yoki boshqa hech narsaga) bog'liq emas — har loyihada mustaqil ishlaydi. RTK Query bilan taqqos: ikkalasi ham server state'ni (kesh, sync, loading) hal qiladi, lekin farq bor: (1) bog'liqlik — RTK Query Redux kerak, TanStack Query mustaqil; (2) yondashuv — RTK Query deklarativ (endpoint'larni oldindancreateApida ta'riflaysiz), TanStack Query imperativroq (useQueryda to'g'ridan query function berasiz — joyida); (3) moslashuvchanlik — TanStack Query ko'proq erkin va keng; (4) community — TanStack Query'ning community'si eng katta (de-facto standart). Ikki nuqta: (1) ikkalasi bir muammoni (server state — 12.3: 2.1) hal qiladi — farq ekotizim va uslubda; (2) Redux loyiha RTK Query (tabiiy, qo'shimcha kutubxona kerak emas); aks holda (Redux'siz, yangi loyiha) TanStack Query (ko'pincha afzal — eng keng, moslashuvchan). Bu bobda TanStack Query'ni chuqur o'rganamiz, chunki u zamonaviy React'ning de-facto server-state standarti.
2.2. Setup — QueryClient va QueryClientProvider
TANSTACK QUERY SETUP (2 qadam):
1. QueryClient — kesh va sozlamalar "miyasi" (bir marta yaratiladi):
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 daqiqa "yangi" (eskirish — 2.4)
retry: 1, // xato 1 marta qayta urinish
},
},
});
2. QueryClientProvider — ilovani o'raydi (Context kabi):
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools /> // DevTools (kesh ko'rinishi — 2.12)
</QueryClientProvider>
┌────────────────────────────────────────────────────────────┐
│ QueryClient: kesh miyasi (sozlama) | Provider: ilovaga beradi│
└────────────────────────────────────────────────────────────┘
QueryClient — bir marta (kesh saqlanadi); Provider — ildizda (Context kabi)
defaultOptions — global sozlama (staleTime, retry — har query'ga — 2.4)Setup — QueryClient va QueryClientProvider — TanStack Query'ni ishga tushirish (ikki qadam). (1)
QueryClient— kesh va global sozlamalarning "miyasi" (bir marta yaratiladi):new QueryClient({ defaultOptions: { queries: { staleTime: 60000, retry: 1 } } })—defaultOptionsbarcha query'ga qo'llanadigan global sozlama (staleTime— ma'lumot qancha vaqt "yangi" hisoblanadi — 2.4;retry— xato bo'lganda necha marta qayta urinish). (2)QueryClientProvider— ilovani o'rab, QueryClient'ni beradi (Context kabi — 12.1):<QueryClientProvider client={queryClient}><App /></QueryClientProvider>, va odatda<ReactQueryDevtools />ham qo'shiladi (kesh holatini ko'rish — 2.12). Ikki nuqta: (1)QueryClient— bir marta yaratiladi (komponent tashqarida yokiuseState/useRefda — har render qayta yaratilmacligi uchun; kesh shu obyektda saqlanadi);Provider— ildizda (main.tsx/App, Context Provider kabi); (2)defaultOptions— global sozlama (har query'ga, lekin har query o'zida ham override qila oladi — 2.4). Bu — TanStack Query'ning minimal, bir martalik sozlamasi (keyin faqatuseQuery/useMutationishlatasiz).
2.3. useQuery — queryKey va queryFn
useQuery — ma'lumot O'QISH (eng asosiy hook):
import { useQuery } from "@tanstack/react-query";
const { data, isPending, error } = useQuery({
queryKey: ["products"], // KESH KALITI (unikal — 2.6)
queryFn: () => fetch("/api/products").then((r) => r.json()), // SO'ROV funksiyasi
});
IKKI ASOSIY QISM:
queryKey — keshni identifikatsiya qiladi (massiv — ["products"], ["product", id])
har xil key — har xil kesh; bir xil key — ulashilgan kesh (dedupe)
queryFn — ma'lumotni qaytaruvchi async funksiya (fetch, axios — ISTALGAN)
Promise qaytaradi (data — uning natijasi)
ARGUMENT bilan (queryKey'da + queryFn'da):
const { data } = useQuery({
queryKey: ["product", id], // id key'ning bir qismi (id o'zgarsa — yangi kesh)
queryFn: () => fetch(`/api/products/${id}`).then((r) => r.json()),
});
queryKey — kesh kaliti (massiv); queryFn — so'rov (istalgan fetch/axios)
queryFn — faqat Promise qaytarsa yetadi (fetch, axios, GraphQL — universal)
useQuery— queryKey va queryFn — TanStack Query'ning eng asosiy hook'i (ma'lumot o'qish).useQuery({ queryKey, queryFn })— ikki asosiy qism: (1)queryKey— keshni identifikatsiya qiluvchi massiv (["products"],["product", id]) — bu kesh kaliti: har xil key — har xil kesh, bir xil key — ulashilgan kesh (deduplication — bir nechta komponent bir xil query'ni ishlatsa, bitta so'rov); (2)queryFn— ma'lumotni qaytaruvchi async funksiya (() => fetch("/api/products").then(r => r.json())) — u Promise qaytarsa yetadi, shuning uchun istalgan so'rov mexanizmi (fetch, axios — 2.18, GraphQL) ishlatsa bo'ladi (RTK Query'ningfetchBaseQuery'sidan moslashuvchanroq). Argument bilan:queryKey: ["product", id](id key'ning bir qismi — id o'zgarsa yangi kesh, yangi so'rov),queryFn: () => fetch(\/api/products/${id}`). Ikki nuqta: (1) **queryKey** — kesh kaliti (massiv — strukturali, dependency bilan — 2.6); (2) **queryFn** — so'rov funksiyasi (Promise qaytarsa yetadi — universal, har qanday fetch/axios/GraphQL bilan).useQuery—useEffect`+fetch'ning 11.5-bob butun og'rig'ini (loading, error, kesh, race, dedupe) bir necha qatorga qisqartiradi.
2.4. Query hayot sikli va keshlash (staleTime, gcTime)
IKKI MUHIM VAQT (TanStack Query keshining asosi):
staleTime — ma'lumot qancha vaqt "YANGI" (fresh) hisoblanadi (default: 0):
- "yangi" davrida keshdan (so'rov YO'Q)
- "eskirgan" (stale) bo'lgach keshdan KO'RSATADI + fonda YANGILAydi (refetch)
gcTime (eski nomi cacheTime) — ishlatilmagan kesh qancha SAQLANADI (default: 5 daqiqa):
- hech bir komponent query'ni ishlatmasa gcTime'dan keyin kesh O'CHIRILADI
HAYOT SIKLI:
fetch ──► fresh (staleTime) ──► stale (eskirgan) ──► [unmount] ──► gcTime ──► o'chirish
(keshdan) (keshdan+fonda (kesh saqlanadi)
yangilash)
STALE-WHILE-REVALIDATE (asosiy strategiya):
eskirgan ma'lumotni DARROV ko'rsatish (keshdan) + fonda yangisini olish
foydalanuvchi kutmaydi (eski ko'radi), keyin yangilanadi (eng yaxshi UX)
staleTime — qancha "yangi" (so'rov yo'q); gcTime — ishlatilmagan kesh saqlanishi
Stale-while-revalidate — eski + fonda yangi (foydalanuvchi hech kutmaydi)Query hayot sikli va keshlash — TanStack Query keshining ikki muhim vaqti.
staleTime— ma'lumot qancha vaqt "yangi" (fresh) hisoblanishi (default: 0 — darrov eskiradi): ma'lumot "yangi" davrida keshdan olinadi (so'rov yuborilmaydi); "eskirgan" (stale) bo'lgach — keshdan ko'rsatiladi (darrov), lekin fonda yangisi olinadi (refetch).gcTime(garbage collection time, eski nomicacheTime) — ishlatilmagan kesh qancha vaqt saqlanishi (default: 5 daqiqa): agar hech bir komponent query'ni ishlatmasa,gcTime'dan keyin kesh o'chiriladi. Hayot sikli: fetch fresh (staleTime davomida keshdan) stale (eskirgan — keshdan + fonda yangilash) [komponent unmount] gcTime davomida saqlanadi o'chirish. Eng muhim strategiya — "stale-while-revalidate": eskirgan ma'lumotni darrov ko'rsatish (keshdan) va fonda yangisini olish — foydalanuvchi hech kutmaydi (eski ma'lumotni ko'radi, keyin jim yangilanadi). Ikki nuqta: (1)staleTime— qancha "yangi" (bu davrda so'rov yo'q — performance);gcTime— ishlatilmagan kesh qancha saqlanishi; (2) stale-while-revalidate — eski + fonda yangi (eng yaxshi UX, hech "yuklanmoqda" oq ekran yo'q kesh bor bo'lganda).staleTime'ni to'g'ri qo'yish (masalan 5 daqiqa kam o'zgaradigan ma'lumot uchun) — keraksiz so'rovlarni keskin kamaytiradi.
2.5. Loading va error holatlari
useQuery HOLATLARI (status):
isPending — yuklanmoqda (hali ma'lumot yo'q — v5'da "isLoading" "isPending")
isError — xato (error bor)
isSuccess — muvaffaqiyat (data bor)
isFetching — har QANDAY yuklanish (birinchi + fonda — RTK Query'dek — 12.3: 2.8)
data — ma'lumot
error — xato obyekti
refetch — qo'lda qayta yuklash
function Products() {
const { data, isPending, isError, error, isFetching } = useQuery({
queryKey: ["products"],
queryFn: fetchProducts,
});
if (isPending) return <Skeleton />; // birinchi yuklanish
if (isError) return <Error message={error.message} />;
return (
<div>
{isFetching && <small>Yangilanmoqda...</small>} {/* fonda 2.4-bob */}
<List items={data} />
</div>
);
}
isPending — birinchi (kesh yo'q); isFetching — har yuklash (fonda — 12.3: 2.8 kabi)
v5: "isLoading" "isPending" (kesh yo'q + fetch bo'layotgan)Loading va error holatlari —
useQueryning status holatlari (RTK Query'dek — 12.3: 2.8). Asosiy holatlar:isPending(yuklanmoqda — hali ma'lumot yo'q; TanStack Query v5'da eskiisLoadingisPendingga o'zgardi),isError(xato),isSuccess(muvaffaqiyat),isFetching(har qanday yuklanish — birinchi + fonda yangilash),data(ma'lumot),error(xato obyekti),refetch(qo'lda qayta yuklash). Naqsh:if (isPending) return <Skeleton/>(birinchi yuklanish),if (isError) return <Error/>, va{isFetching && <small>Yangilanmoqda...</small>}(fonda yangilash indikatori — 2.4). Ikki nuqta: (1)isPending— birinchi yuklanish (kesh yo'q — to'liq skeleton);isFetching— har yuklash (fonda — kichik indikator — 12.3: 2.8 bilan bir xil g'oya); (2) v5 o'zgarishi — eskiisLoadingendiisPending(kesh yo'q + fetch bo'layotgan holat);isLoadinghali bor lekinisPending && isFetchingga teng. Bu holatlar 11.15: 2.8 dagi 4 holatni (loading/error/empty/data) avtomatik boshqaradi —useEffect+fetch'dagi qo'ldauseState(loading/error/data) butunlay yo'qoladi.
2.6. Query keys — struktura va dependency
QUERY KEY — keshni identifikatsiya qiluvchi MASSIV (struktura muhim):
ODDIY MURAKKAB (umumiydan aniqga):
["products"] — barcha mahsulotlar
["products", { category: "phone" }] — filtrlangan (obyekt — argument)
["products", productId] — bitta mahsulot
["products", productId, "reviews"] — mahsulotning sharhlari (ichma-ich)
DEPENDENCY (key argument o'zgarsa — yangi so'rov):
const { data } = useQuery({
queryKey: ["products", category, page], // category yoki page o'zgarsa — yangi so'rov
queryFn: () => fetchProducts(category, page),
});
// category "phone""laptop" — yangi kesh, yangi so'rov (yoki kesh bo'lsa keshdan)
KONVENSIYA (Query Key Factory — tartibli):
const productKeys = {
all: ["products"],
list: (filters) => [...productKeys.all, "list", filters],
detail: (id) => [...productKeys.all, "detail", id],
};
Query key — MASSIV (umumiydan aniqga — invalidation uchun ierarxiya — 2.9)
Key'dagi o'zgaruvchi (id/filtr) — dependency (o'zgarsa — yangi so'rov, avtomatik)Query keys — struktura va dependency — TanStack Query'ning markaziy tushunchasi (kesh va invalidation key'ga tayanadi). Query key — keshni identifikatsiya qiluvchi massiv; struktura muhim (umumiydan aniqga):
["products"](barcha),["products", { category: "phone" }](filtrlangan — obyekt argument),["products", productId](bitta),["products", productId, "reviews"](ichma-ich — sharhlar). Dependency — key'dagi o'zgaruvchi o'zgarsa, TanStack Query avtomatik yangi so'rov yuboradi:queryKey: ["products", category, page]—categoryyokipageo'zgarsa, yangi kesh + yangi so'rov (yoki o'sha key kesh'i bo'lsa keshdan). Bu —useEffectdependency'siga o'xshaydi, lekin avtomatik (key o'zgarsa query qayta ishlaydi). Konvensiya — Query Key Factory (tartibli, xatosiz):const productKeys = { all: ["products"], list: (f) => [...productKeys.all, "list", f], detail: (id) => [...productKeys.all, "detail", id] }— key'larni markazlashgan obyektda saqlash (qo'lda key yozish o'rniga — typo va invalidation xatolaridan saqlaydi). Ikki nuqta: (1) query key — massiv (umumiydan aniqga — ierarxik invalidation uchun —["products"]ni bekor qilsa, barcha["products", ...]ham bekor — 2.9); (2) key'dagi o'zgaruvchi — dependency (o'zgarsa avtomatik yangi so'rov — filtr, sahifa, id). Key — TanStack Query'ni to'g'ri ishlatishning asosi.
2.7. Dependent va parallel queries
PARALLEL QUERIES — bir nechta query BIR VAQTDA (mustaqil — birga yuklanadi):
const users = useQuery({ queryKey: ["users"], queryFn: fetchUsers });
const posts = useQuery({ queryKey: ["posts"], queryFn: fetchPosts });
// ikkalasi PARALLEL yuklanadi (bir-birini kutmaydi — tez)
// Dinamik parallel (massiv) — useQueries:
const results = useQueries({
queries: userIds.map((id) => ({ queryKey: ["user", id], queryFn: () => fetchUser(id) })),
});
DEPENDENT QUERIES — biri ikkinchisiga BOG'LIQ (ketma-ket — enabled):
const { data: user } = useQuery({ queryKey: ["user", email], queryFn: () => fetchUser(email) });
const { data: projects } = useQuery({
queryKey: ["projects", user?.id],
queryFn: () => fetchProjects(user.id),
enabled: !!user?.id, // user kelguncha KUTadi (user bo'lganda ishlaydi)
});
// avval user, keyin uning projects'i (ketma-ket — bog'liq)
Parallel — mustaqil query'lar birga (tez); useQueries — dinamik massiv
Dependent — enabled bilan (oldingi natija kelguncha kutadi — ketma-ket)Dependent va parallel queries — bir nechta query'ni boshqarish. Parallel queries — bir nechta mustaqil query bir vaqtda (birga) yuklanadi:
const users = useQuery({...}); const posts = useQuery({...})— ikkalasi parallel (bir-birini kutmaydi — eng tez). Dinamik parallel (soni o'zgaruvchi) uchunuseQueries:useQueries({ queries: userIds.map(id => ({ queryKey: ["user", id], queryFn: () => fetchUser(id) })) })— massivdagi har id uchun query. Dependent (ketma-ket) queries — biri ikkinchisiga bog'liq (oldingi natija kerak):const { data: user } = useQuery({...}); const { data: projects } = useQuery({ queryKey: ["projects", user?.id], queryFn: () => fetchProjects(user.id), enabled: !!user?.id })—enabledopsiyasi:userkelguncha ikkinchi query kutadi (!!user?.idfalse bo'lganda query ishlamaydi,userkelgach — ishga tushadi). Ikki nuqta: (1) parallel — mustaqil query'lar birga (tez — bir-birini kutmaydi); dinamik soni uchunuseQueries; (2) dependent —enabledbilan (oldingi natija kelguncha kutadi — masalan avval foydalanuvchini olib, keyin uning ma'lumotlarini).enabled— query'ni shartli ishga tushirish uchun ham ishlatiladi (masalan modal ochiq bo'lganda —enabled: isOpen). Bu — murakkab ma'lumot bog'liqliklarini boshqarishning toza usuli.
2.8. useMutation — server o'zgartirish
useMutation — server ma'lumotini O'ZGARTIRISH (POST/PUT/DELETE):
const mutation = useMutation({
mutationFn: (newProduct) => fetch("/api/products", {
method: "POST", body: JSON.stringify(newProduct),
}).then((r) => r.json()),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["products"] }); // kesh yangilash 2.9-bob
toast.success("Qo'shildi");
},
onError: (error) => toast.error(error.message),
});
// Trigger:
mutation.mutate(newProduct); // so'rov yuborish
// yoki: await mutation.mutateAsync(newProduct); // Promise (try/catch)
HOLATLAR:
mutation.isPending — yuborilmoqda (tugma disable)
mutation.isSuccess — muvaffaqiyat
mutation.isError — xato
mutation.mutate — trigger funksiya
mutationFn — o'zgartirish so'rovi; onSuccess/onError — natijaga reaksiya
mutate (trigger) — foydalanuvchi harakatida (tugma); query — avtomatik (mount'da)
useMutation— server o'zgartirish — ma'lumotni o'zgartirish (POST/PUT/DELETE).useMutation({ mutationFn, onSuccess, onError }): (1)mutationFn— o'zgartirish so'rovi ((newProduct) => fetch("/api/products", { method: "POST", body })); (2)onSuccess— muvaffaqiyat natijasiga reaksiya (odatda keshni yangilash —queryClient.invalidateQueries(...)— 2.9, va toast); (3)onError— xatoga reaksiya. Trigger:mutation.mutate(newProduct)(so'rov yuborish — foydalanuvchi tugmani bosganda), yokiawait mutation.mutateAsync(newProduct)(Promise sifatida — try/catch bilan). Holatlar:mutation.isPending(yuborilmoqda — tugmani disable qilish),isSuccess,isError,mutate(trigger). Ikki nuqta: (1)mutationFn— o'zgartirish so'rovi;onSuccess/onError— natijaga reaksiya (kesh yangilash, navigatsiya, toast); (2) query avtomatik ishlaydi (komponent mount'da), mutation trigger bilan (mutate— foydalanuvchi harakatida).useMutation— server'ni o'zgartirish va undan keyin keshni sinxron tutishning standart usuli (12.3 RTK Query mutation bilan bir xil g'oya, lekin imperativroq).
2.9. Invalidation va cache update
MUTATION'dan keyin keshni yangilash — 2 usul:
USUL 1 — INVALIDATE (eng keng — query'ni "eskirgan" deb belgila qayta yuklanadi):
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["products"] }); // ["products"]* qayta yuklanadi
}
// eng oddiy, ishonchli (server'dan eng yangi); kichik ortiqcha so'rov
USUL 2 — SET QUERY DATA (keshni TO'G'RIDAN yangilash — so'rovsiz, tez):
onSuccess: (newProduct) => {
queryClient.setQueryData(["products"], (old) => [...old, newProduct]); // keshga qo'sh
}
// so'rov yo'q (tezroq), lekin qo'lda (server bilan farq xavfi)
IERARXIK INVALIDATION (key massivi — 2.6):
invalidateQueries({ queryKey: ["products"] }) // ["products"], ["products", id], ... HAMMASI
┌────────────────────────────────────────────────────────────┐
│ invalidate: "eskirgan" belgi qayta yuklash (ishonchli) │
│ setQueryData: keshni to'g'ridan (so'rovsiz, tez — ehtiyot) │
└────────────────────────────────────────────────────────────┘
invalidate — eng keng (qayta yuklash — server eng yangi); setQueryData — tez (qo'lda)
Ierarxik key — umumiy invalidate barcha ostki query'ni bekor qiladi (2.6)Invalidation va cache update — mutation'dan keyin keshni yangilash (server bilan sinxron tutish). Ikki usul: Usul 1 —
invalidateQueries(eng keng, eng ishonchli):queryClient.invalidateQueries({ queryKey: ["products"] })— bu query'ni (va barcha ostki["products", ...]query'larini — ierarxik — 2.6) "eskirgan" deb belgilaydi ular qayta yuklanadi (server'dan eng yangi ma'lumot). Oddiy, ishonchli, lekin kichik ortiqcha so'rov (server'ga boradi). Usul 2 —setQueryData(keshni to'g'ridan yangilash — so'rovsiz, tezroq):queryClient.setQueryData(["products"], (old) => [...old, newProduct])— keshni to'g'ridan o'zgartiradi (so'rov yuborilmaydi — tezroq), lekin qo'lda (server bilan farq xavfi — masalan server qo'shimcha maydon qo'shca, kesh eskirgan bo'ladi). Ierarxik invalidation:invalidateQueries({ queryKey: ["products"] })—["products"]dan boshlangan barcha query'ni bekor qiladi (["products", id],["products", "list", filter]— hammasi — 2.6). Ikki nuqta: (1)invalidate— eng keng (qayta yuklash — server eng yangi, ishonchli);setQueryData— tez (qo'lda kesh yangilash — ehtiyot bilan, optimistic uchun — 2.10); (2) ierarxik key — umumiyinvalidatebarcha ostki query'ni bekor qiladi (key strukturasining foydasi). Amaldainvalidateko'pincha yetadi (oddiy, ishonchli).
2.10. Optimistic updates
OPTIMISTIC UPDATE — server javobini KUTMASDAN UI'ni darrov yangilash (12.3: 2.9):
const mutation = useMutation({
mutationFn: toggleLike,
onMutate: async (productId) => { // MUTATION'dan OLDIN (darrov):
await queryClient.cancelQueries({ queryKey: ["products"] }); // joriy so'rovni to'xtat
const previous = queryClient.getQueryData(["products"]); // eski'ni sakla (rollback uchun)
queryClient.setQueryData(["products"], (old) => // UI darrov yangila:
old.map((p) => p.id === productId ? { ...p, liked: !p.liked } : p)
);
return { previous }; // context (rollback uchun)
},
onError: (err, productId, context) => {
queryClient.setQueryData(["products"], context.previous); // xato ROLLBACK
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["products"] }); // oxirida server bilan sync
},
});
JARAYON: onMutate (darrov UI) server onError (rollback) yoki onSettled (sync)
Optimistic — onMutate (darrov UI) + onError (rollback) + onSettled (sync)
"Yoqdi", "saqlash" — darrov his (server kutmaydi); xato bo'lsa orqagaOptimistic updates — server javobini kutmasdan UI'ni darrov yangilash (12.3: 2.9 — TanStack Query versiyasi).
useMutationda uch callback bilan: (1)onMutate— mutation'dan oldin (darrov ishlaydi): joriy so'rovlarni to'xtatish (cancelQueries— eski so'rov javobi UI'ni buzmasin), eski ma'lumotni saqlash (getQueryData— rollback uchun), va keshni darrov yangilash (setQueryData— UI o'zgaradi, server javobini kutmasdan);previousni context sifatida qaytaradi. (2)onError— xato bo'lsa rollback (setQueryData(..., context.previous)— eski holatga qaytarish). (3)onSettled— oxirida (muvaffaqiyat yoki xato — baribir) server bilan sinxronlash (invalidateQueries). Jarayon:onMutate(darrov UI) server so'rovonError(rollback) yokionSettled(sync). Ikki nuqta: (1) optimistic —onMutate(darrov UI) +onError(rollback) +onSettled(sync) — uchchovi birga; (2) "yoqdi", "saqlash", "qo'shish" kabi amallar uchun ideal (foydalanuvchi darrov natijani ko'radi — server kutmaydi; xato bo'lsa jim orqaga qaytadi). Bu — eng silliq, eng tez his beruvchi UX naqshi (zamonaviy ilovalarning belgisi — Instagram "like", Twitter "retweet" kabi).
2.11. Pagination va infinite queries
PAGINATION (sahifalash — keepPreviousData bilan silliq):
const { data, isFetching } = useQuery({
queryKey: ["products", page], // page key'da (o'zgarsa — yangi sahifa)
queryFn: () => fetchProducts(page),
placeholderData: keepPreviousData, // yangi sahifa yuklanganda eski'ni ushla (sakramaydi)
});
// "Keyingi" bossa — eski sahifa ko'rinib turadi, yangisi fonda (silliq)
INFINITE QUERIES (cheksiz scroll — "ko'proq yuklash"):
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ["products"],
queryFn: ({ pageParam }) => fetchProducts(pageParam),
initialPageParam: 1,
getNextPageParam: (lastPage) => lastPage.nextCursor, // keyingi sahifa param'i
});
// data.pages — barcha yuklangan sahifalar; fetchNextPage() — keyingisini yukla
// cheksiz scroll / "Ko'proq" tugmasi (feed, ro'yxat)
Pagination — keepPreviousData (sahifa almashganda sakramaydi — silliq)
useInfiniteQuery — cheksiz scroll (fetchNextPage + getNextPageParam — feed)Pagination va infinite queries — katta ro'yxatlarni sahifalash va cheksiz scroll. Pagination (sahifalash) —
queryKeydapageni qo'shasiz (["products", page]— page o'zgarsa yangi sahifa), vaplaceholderData: keepPreviousData— yangi sahifa yuklanayotganda eski sahifani ushlab turadi (UI sakramaydi — "Keyingi" bosganda eski ko'rinib turadi, yangisi fonda yuklanadi — silliq UX, bo'sh ekran yo'q). Infinite queries (cheksiz scroll — "ko'proq yuklash" / feed) —useInfiniteQuery:queryFn: ({ pageParam }) => fetchProducts(pageParam),initialPageParam: 1(boshlang'ich),getNextPageParam: (lastPage) => lastPage.nextCursor(keyingi sahifa parametri — null bo'lsa oxiri);data.pages(barcha yuklangan sahifalar massivi),fetchNextPage()(keyingisini yuklash),hasNextPage(yana bormi). Bu — cheksiz feed (Instagram, Twitter), "Ko'proq yuklash" tugmasi, yoki scroll bilan avtomatik yuklash (Intersection Observer — 2.17 — bilan) uchun ideal. Ikki nuqta: (1) pagination —keepPreviousData(sahifa almashganda sakramaydi — silliq); (2)useInfiniteQuery— cheksiz scroll (fetchNextPage+getNextPageParam— feed, katta ro'yxat). TanStack Query bu murakkab naqshlarni (qo'lda juda qiyin) bir necha qatorga qisqartiradi — bu uning eng kuchli tomonlaridan biri.
2.12. Prefetching, DevTools va RTK Query bilan yakuniy qaror
PREFETCHING — ma'lumotni OLDINDAN yuklash (foydalanuvchi so'rashidan oldin — 11.8: 2.10):
// hover'da keyingi sahifa ma'lumotini oldindan yukla (darrov ochilsin):
const prefetch = () => queryClient.prefetchQuery({ queryKey: ["product", id], queryFn: ... });
<Link onMouseEnter={prefetch} to={`/products/${id}`}>...</Link>
REACT QUERY DEVTOOLS (kesh holatini ko'pish — ajoyib):
<ReactQueryDevtools initialIsOpen={false} /> // har query, kesh, holat — vizual
RTK QUERY vs TANSTACK QUERY (yakuniy qaror — 2.1):
┌──────────────────────────────────────────────────────────┐
│ Redux loyiha (allaqachon RTK) RTK Query (tabiiy) │
│ Redux'siz / yangi loyiha TanStack Query (eng keng) │
│ Ikkalasi server state'ni zo'r hal qiladi (falsafa bir xil)│
└──────────────────────────────────────────────────────────┘
Prefetching — hover'da oldindan (navigatsiya darrov — UX); DevTools — kesh debug
Yakuniy: server state har doim Query (RTK yoki TanStack); client Redux/Zustand/ContextPrefetching, DevTools va yakuniy qaror — TanStack Query'ning qolgan imkoniyatlari va qaror. Prefetching — ma'lumotni foydalanuvchi so'rashidan oldin yuklash (11.8: 2.10 — kechikishni yo'qotish):
queryClient.prefetchQuery({ queryKey, queryFn })— masalan<Link onMouseEnter={prefetch}>(hover'da keyingi sahifa ma'lumotini fonda yukla — bosganda darrov ochilsin). React Query DevTools (<ReactQueryDevtools />) — kesh holatini vizual ko'rsatadi (har query, uning holati — fresh/stale/fetching, kesh ma'lumoti) — debugging uchun ajoyib (RTK Query'ning Redux DevTools'idek). RTK Query vs TanStack Query yakuniy qaror 2.1-bob: Redux loyiha (allaqachon RTK ishlatsa) RTK Query (tabiiy, qo'shimcha kutubxona kerak emas); Redux'siz yoki yangi loyiha TanStack Query (eng keng, moslashuvchan); ikkalasi ham server state'ni zo'r hal qiladi (falsafa bir xil — 12.3: 2.1). Ikki yakuniy nuqta: (1) prefetching — hover'da oldindan yuklash (navigatsiya darrov — UX); DevTools — kesh debug (kuchli vosita); (2) eng muhim qoida — server state har doim Query (RTK yoki TanStack), client state esa Redux/Zustand/Context (12.1: 2.10). Bu ajratishni anglash — zamonaviy frontend arxitekturasining cho'qqisi (server va client state — har xil muammo, har xil vosita). TanStack Query — bugun har React loyihada (Redux bo'lsin yoki yo'q) ishlatiladigan, server bilan ishlashni tubdan soddalashtirgan vosita.
2.13. useQuery opsiyalari — select, refetch, retry, status
useQuery'ning MUHIM QO'SHIMCHA OPSIYALARI:
select — data'ni TRANSFORMATSIYA qilish (faqat kerakli qismi/formasi):
const { data } = useQuery({
queryKey: ["products"],
queryFn: fetchProducts,
select: (data) => data.map((p) => p.name), // faqat nomlar (butun massiv emas)
});
select faqat DATA'ni o'zgartiradi (kesh butun holicha saqlanadi)
select referensiyasi barqaror bo'lsa — keraksiz render yo'q (memoized)
refetch* — QACHON avtomatik qayta yuklash (default: hammasi true):
refetchOnWindowFocus: true — oyna fokusga qaytganda (tab almashib qaytganda)
refetchOnMount: true — komponent mount bo'lganda (stale bo'lsa)
refetchOnReconnect: true — internet uzilib-ulanganda
refetchInterval: 5000 — har 5s (polling — real-time'ga yaqin)
retry — xato bo'lganda qayta urinish (default: 3):
retry: 3 — 3 marta (exponential backoff bilan)
retry: false — urinmaslik (masalan 404'da)
retry: (count, err) => err.status !== 404 && count < 2 // shartli
status / fetchStatus — ikki alohida holat (v5):
status: "pending" | "error" | "success" (DATA holati)
fetchStatus: "fetching" | "paused" | "idle" (SO'ROV holati)
ikki o'q: ma'lumot bormi (status) + so'rov ketyaptimi (fetchStatus)
select — data transform (kesh emas); refetch* — qachon yangilash; retry — xatoda
status (data) + fetchStatus (so'rov) — ikki mustaqil o'q (v5)
useQueryopsiyalari —queryKey/queryFndan tashqari kundalik ishlatiladigan sozlamalar.select—datani transformatsiya qiladi (faqat kerakli qismini/formasini olish):select: (data) => data.map(p => p.name)— komponent butun massiv o'rniga faqat nomlarni oladi. Muhim:selectfaqatdatani o'zgartiradi (kesh butun holicha saqlanadi — boshqa komponent to'liq ma'lumotni olishi mumkin), va agarselectreferensiyasi barqaror bo'lsa (komponent tashqarida yokiuseCallback), transform faqat ma'lumot o'zgarganda ishlaydi — keraksiz render bo'lmaydi (memoizatsiya).refetchOnWindowFocus(defaulttrue) — oyna fokusga qaytganda avtomatik qayta yuklash (foydalanuvchi boshqa tab'dan qaytganda ma'lumot yangilanadi — juda foydali standart);refetchOnMount— komponent mount'da (stale bo'lsa);refetchOnReconnect— internet uzilib qayta ulanganda;refetchInterval: 5000— har 5 soniyada avtomatik (polling — real-time'ga yaqin, masalan jonli hisoblagich).retry— xato bo'lganda qayta urinish (default: 3 marta, exponential backoff — kutish oralig'i ortib boradi):retry: false(umuman urinmaslik — masalan 404 uchun mantiqsiz), yoki shartli funksiya (retry: (count, err) => err.status !== 404 && count < 2).statusvafetchStatus(v5'da ikki alohida o'q):status— ma'lumot holati ("pending"|"error"|"success"),fetchStatus— so'rov holati ("fetching"|"paused"— internet yo'q |"idle"). Ikki nuqta: (1)select— data transform (kesh emas, memoized);refetch*— qachon avtomatik yangilash (fokus/mount/reconnect/interval);retry— xatoda qayta urinish; (2)status(ma'lumot bormi) +fetchStatus(so'rov ketyaptimi) — ikki mustaqil o'q;isPending/isFetchingshulardan hosil bo'ladi.
2.14. Query cancellation — AbortSignal
QUERY CANCELLATION — bekor qilingan so'rovni TO'XTATISH (resurs tejash):
queryFn AbortSignal oladi (TanStack Query avtomatik beradi):
const { data } = useQuery({
queryKey: ["products", search],
queryFn: ({ signal }) => fetch(`/api/products?q=${search}`, { signal })
.then((r) => r.json()), // signal — fetch'ga uzatiladi
});
key o'zgarsa (search yangi) yoki komponent unmount — ESKI so'rov BEKOR qilinadi
axios ham qo'llaydi: axios.get(url, { signal })
cancelQueries — QO'LDA bekor qilish (optimistic'da — 2.10):
await queryClient.cancelQueries({ queryKey: ["products"] });
signal'ni queryFn'dan fetch/axios'ga uzat — eski so'rov avtomatik bekor
Tez o'zgaruvchan key (qidiruv) — eski so'rovlar bekor (race yo'q, tejamli)Query cancellation — bekor qilingan so'rovni to'xtatish (resurs tejash va race'ni yo'qotish). TanStack Query
queryFnga avtomatikAbortSignalberadi:queryFn: ({ signal }) => fetch(url, { signal })—signalnifetchga (yokiaxiosga —axios.get(url, { signal })) uzatsangiz, query kaliti o'zgarganda (masalan qidiruv matni yangilanganda) yoki komponent unmount bo'lganda eski so'rov avtomatik bekor qilinadi. Bu tez o'zgaruvchan query'lar (jonli qidiruv — har harf bosishda yangi so'rov) uchun juda muhim: eski, endi kerak bo'lmagan so'rovlar bekor bo'ladi — race condition yo'qoladi (11.5'dagiuseEffect+fetch'ning eng katta og'rig'i) va tarmoq tejaladi. Qo'lda bekor qilish uchunqueryClient.cancelQueries({ queryKey })ishlatiladi (optimistic update'daonMutateda — eski so'rov javobi optimistic UI'ni buzmasin — 2.10). Ikki nuqta: (1)signalniqueryFndanfetch/axiosga uzatish — key o'zgarsa/unmount bo'lsa eski so'rov avtomatik bekor; (2) tez o'zgaruvchan key (qidiruv, filtr) uchun bu race'ni yo'qotadi va tarmoqni tejaydi — deyarli bepul (bir qatorlik{ signal }).
2.15. Suspense mode — useSuspenseQuery
SUSPENSE MODE — loading holatini <Suspense> ga topshirish 11.8-bob:
ODATIY (isPending qo'lda tekshiriladi):
const { data, isPending } = useQuery({ queryKey: ["user"], queryFn: fetchUser });
if (isPending) return <Spinner />;
SUSPENSE (isPending YO'Q — Suspense ushlaydi):
import { useSuspenseQuery } from "@tanstack/react-query";
const { data } = useSuspenseQuery({ queryKey: ["user"], queryFn: fetchUser });
// data — DARROV mavjud (undefined emas — TypeScript ham biladi)
// loading eng yaqin <Suspense fallback={<Spinner/>}> ushlaydi
// error eng yaqin <ErrorBoundary> ushlaydi
<ErrorBoundary fallback={<Error/>}>
<Suspense fallback={<Spinner/>}>
<UserProfile /> {/* useSuspenseQuery ichida */}
</Suspense>
</ErrorBoundary>
useSuspenseQuery — data doim bor (isPending yo'q); loading/error deklarativ
Suspense 11.8-bob + ErrorBoundary bilan — toza, deklarativ loading/errorSuspense mode — loading holatini komponent ichida
isPendingbilan tekshirish o'rniga React'ning<Suspense>mexanizmiga 11.8-bob topshirish.useSuspenseQuery— odatiyuseQuerydan farqi: uisPendingqaytarmaydi —datahar doim mavjud (undefinedemas — bu TypeScript'da ham foydali:datatypiProductbo'ladi,Product | undefinedemas — ortiqcha tekshiruvlar yo'q). Yuklanish holatini eng yaqin<Suspense fallback={...}>ushlaydi (fallback ko'rsatiladi), xatoni esa eng yaqin<ErrorBoundary>11.8-bob ushlaydi. Naqsh:<ErrorBoundary><Suspense fallback={<Spinner/>}><UserProfile/></Suspense></ErrorBoundary>—UserProfileichidauseSuspenseQueryishlaydi, loading/error markazlashgan (deklarativ) boshqariladi. Ikki nuqta: (1)useSuspenseQuery—datadoim bor (isPendingyo'q), loading<Suspense>, error<ErrorBoundary>ga topshiriladi; (2) bu Suspense-asosli arxitektura 11.8-bob uchun toza yechim — ayniqsa Next.js va React Server Components bilan tabiiy. Ehtiyot: bir nechtauseSuspenseQuerybir komponentda ketma-ket (waterfall) bo'lib qolmasligi uchunuseSuspenseQueries(parallel) ishlatiladi.
2.16. SSR, hydration va TypeScript
SSR / HYDRATION (Next.js — 13-QISM, server'da yuklab, client'ga uzatish):
// Server: ma'lumotni oldindan yukla:
const queryClient = new QueryClient();
await queryClient.prefetchQuery({ queryKey: ["products"], queryFn: fetchProducts });
// dehydrate HTML bilan client'ga <HydrationBoundary state={dehydrate(qc)}>
// client "yuklanmoqda" ko'rmaydi (server'da tayyor — SEO + tezlik)
TYPESCRIPT (queryFn tipi data tipi avtomatik):
async function fetchProduct(id: string): Promise<Product> { ... }
const { data } = useQuery({
queryKey: ["product", id],
queryFn: () => fetchProduct(id), // Promise<Product>
});
// data: Product | undefined (isPending true'da undefined — TS majbur qiladi tekshirishga)
SSR — server'da prefetch + dehydrate/HydrationBoundary (Next.js — 13-QISM)
TS — queryFn qaytaruv tipidan data tipi avtomatik (Product | undefined)SSR, hydration va TypeScript — ilg'or, ammo muhim mavzular. SSR/hydration (Next.js — 13-QISM'da chuqur): ma'lumotni server'da oldindan yuklab (
queryClient.prefetchQuery), keshni dehydrate qilib (JSON'ga aylantirib) HTML bilan client'ga uzatish, so'ng client'da<HydrationBoundary state={dehydrate(queryClient)}>bilan tiklash — natijada foydalanuvchi "yuklanmoqda" holatini ko'rmaydi (server'da ma'lumot tayyor keladi — SEO va birinchi ko'rinish tezligi yaxshilanadi). Bu Next.js App Router bilan tabiiy ishlaydi (server component'da prefetch, client component'dauseQuery— kesh tayyor). TypeScript — TanStack Query to'liq tip xavfsiz:queryFnning qaytaruv tipidan (Promise<Product>)datatipi avtomatik kelib chiqadi (Product | undefined—isPendingtruebo'lgandaundefined, shuning uchun TSdatani ishlatishdan oldin tekshirishga majbur qiladi — bu xavfsizlik).useSuspenseQuerybilandatato'g'ridanProduct2.15-bob. Generic qo'lda ham beriladi (useQuery<Product[]>), lekinqueryFnni to'g'ri tiplasangiz — kerak emas. Ikki nuqta: (1) SSR — server'daprefetchQuery+dehydrate/HydrationBoundary(Next.js — 13-QISM cross-ref); (2) TS —queryFnqaytaruv tipidandatatipi avtomatik (Product | undefined— tekshirishga majbur, xavfsiz). Bu ikki xususiyat TanStack Query'ni katta, production ilovalarda ishonchli qiladi.
3. Sintaksis — tez ma'lumotnoma
SETUP 2.2-bob: new QueryClient(); <QueryClientProvider client={qc}><App/></QueryClientProvider>
useQuery 2.3-bob: useQuery({ queryKey: ["x", id], queryFn: () => fetch(...).then(r=>r.json()) })
HOLAT 2.5-bob: { data, isPending, isError, error, isFetching, refetch }
KESH 2.4-bob: staleTime (qancha yangi) | gcTime (ishlatilmagan saqlanishi)
KEY 2.6-bob: ["products"] | ["products", id] | ["products", { filter }]
ENABLED 2.7-bob: useQuery({ ..., enabled: !!user?.id }) // shartli/dependent
useMutation 2.8-bob: useMutation({ mutationFn, onSuccess: () => qc.invalidateQueries(...) })
mutation.mutate(data) / await mutation.mutateAsync(data)
INVALIDATE 2.9-bob: queryClient.invalidateQueries({ queryKey: ["products"] })
OPTIMISTIC 2.10-bob: onMutate (setQueryData) + onError (rollback) + onSettled (invalidate)
INFINITE 2.11-bob: useInfiniteQuery({ queryFn:({pageParam}), getNextPageParam }) + fetchNextPage
SELECT 2.13-bob: useQuery({ ..., select: (data) => data.map(x => x.name) }) // transform
REFETCH 2.13-bob: refetchOnWindowFocus | refetchInterval | retry: 3 | staleTime
CANCEL 2.14-bob: queryFn: ({ signal }) => fetch(url, { signal }) // avtomatik bekor
SUSPENSE 2.15-bob: useSuspenseQuery({ queryKey, queryFn }) // data doim bor + <Suspense>4. Batafsil kod namunalari
Misol 1 — Setup (QueryClient + Provider — 2.2)
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const queryClient = new QueryClient({
defaultOptions: {
queries: { staleTime: 60 * 1000, retry: 1, refetchOnWindowFocus: true },
},
});
function Root() {
return (
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} /> {/* kesh DevTools 2.12-bob */}
</QueryClientProvider>
);
}Misol 2 — useQuery (asosiy — 2.3, 2.5)
import { useQuery } from "@tanstack/react-query";
async function fetchProducts(): Promise<Product[]> {
const res = await fetch("/api/products");
if (!res.ok) throw new Error("Server xatosi");
return res.json();
}
function Products() {
const { data, isPending, isError, error, isFetching } = useQuery({
queryKey: ["products"],
queryFn: fetchProducts,
});
if (isPending) return <Skeleton />;
if (isError) return <Error message={error.message} />;
if (!data.length) return <Empty />;
return (
<div>
{isFetching && <small>Yangilanmoqda...</small>} {/* fonda 2.4-bob */}
<ul>{data.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
</div>
);
}
// Hech qanday useEffect/useState/loading qo'lda yo'q — TanStack Query AVTOMATIKMisol 3 — Argument va custom hook (2.3, 2.6)
// Custom hook — query'ni o'rab (qayta ishlatiladigan — 11.7):
export function useProduct(id: string) {
return useQuery({
queryKey: ["product", id], // id key'da (cache key — 2.6)
queryFn: () => fetch(`/api/products/${id}`).then((r) => r.json()),
enabled: !!id, // id bo'lganda (2.7)
});
}
// Ishlatish:
function ProductDetail({ id }: { id: string }) {
const { data: product, isPending } = useProduct(id);
if (isPending) return <Spinner />;
return <h1>{product.name}</h1>;
}Misol 4 — Query key factory (2.6)
// Markazlashgan key'lar (typo va invalidation xatolaridan saqlaydi):
export const productKeys = {
all: ["products"] as const,
lists: () => [...productKeys.all, "list"] as const,
list: (filters: ProductFilters) => [...productKeys.lists(), filters] as const,
details: () => [...productKeys.all, "detail"] as const,
detail: (id: string) => [...productKeys.details(), id] as const,
};
// Ishlatish:
useQuery({ queryKey: productKeys.list({ category: "phone" }), queryFn: ... });
useQuery({ queryKey: productKeys.detail("5"), queryFn: ... });
// Invalidation: queryClient.invalidateQueries({ queryKey: productKeys.all }); // hammasi (2.9)Misol 5 — Dependent query (enabled — 2.7)
function UserProjects({ email }: { email: string }) {
// 1. Avval user (email bo'yicha):
const { data: user } = useQuery({
queryKey: ["user", email],
queryFn: () => fetchUserByEmail(email),
});
// 2. Keyin user'ning projects'i (user kelguncha kutadi):
const { data: projects, isPending } = useQuery({
queryKey: ["projects", user?.id],
queryFn: () => fetchProjects(user!.id),
enabled: !!user?.id, // user bo'lganda ishlaydi (2.7)
});
if (!user) return <p>Foydalanuvchi yuklanmoqda...</p>;
if (isPending) return <p>Loyihalar yuklanmoqda...</p>;
return <ul>{projects?.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}Misol 6 — Parallel queries (2.7)
function Dashboard() {
const users = useQuery({ queryKey: ["users"], queryFn: fetchUsers });
const orders = useQuery({ queryKey: ["orders"], queryFn: fetchOrders });
const stats = useQuery({ queryKey: ["stats"], queryFn: fetchStats });
// uchchovi PARALLEL yuklanadi (bir-birini kutmaydi — tez — 2.7)
if (users.isPending || orders.isPending || stats.isPending) return <Skeleton />;
return <div>{/* users.data, orders.data, stats.data */}</div>;
}Misol 7 — useMutation va invalidation (2.8, 2.9)
import { useMutation, useQueryClient } from "@tanstack/react-query";
function AddProduct() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newProduct: Partial<Product>) =>
fetch("/api/products", { method: "POST", body: JSON.stringify(newProduct) }).then((r) => r.json()),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["products"] }); // ro'yxat qayta yuklanadi (2.9)
toast.success("Qo'shildi");
},
onError: (error) => toast.error(error.message),
});
return (
<button onClick={() => mutation.mutate({ name: "Yangi" })} disabled={mutation.isPending}>
{mutation.isPending ? "Yuborilmoqda..." : "+ Qo'shish"}
</button>
);
}Misol 8 — Optimistic update (2.10)
function useLikeProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => fetch(`/api/products/${id}/like`, { method: "POST" }),
onMutate: async (id) => {
await queryClient.cancelQueries({ queryKey: ["products"] }); // so'rovni to'xtat
const previous = queryClient.getQueryData<Product[]>(["products"]); // eski'ni sakla
queryClient.setQueryData<Product[]>(["products"], (old) => // UI darrov:
old?.map((p) => (p.id === id ? { ...p, liked: !p.liked } : p))
);
return { previous };
},
onError: (err, id, context) => {
queryClient.setQueryData(["products"], context?.previous); // rollback (2.10)
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["products"] }); // sync
},
});
}
// "Yoqdi" darrov (server kutmasdan); xato bo'lsa orqaga (2.10)Misol 9 — Pagination (keepPreviousData — 2.11)
import { keepPreviousData } from "@tanstack/react-query";
function ProductsPaged() {
const [page, setPage] = useState(1);
const { data, isFetching, isPlaceholderData } = useQuery({
queryKey: ["products", page],
queryFn: () => fetchProducts(page),
placeholderData: keepPreviousData, // eski sahifa ushlanadi (sakramaydi — 2.11)
});
return (
<div>
<ul style={{ opacity: isFetching ? 0.6 : 1 }}>
{data?.items.map((p: Product) => <li key={p.id}>{p.name}</li>)}
</ul>
<button onClick={() => setPage((p) => p - 1)} disabled={page === 1}>Oldingi</button>
<button onClick={() => setPage((p) => p + 1)} disabled={!data?.hasMore || isPlaceholderData}>
Keyingi
</button>
</div>
);
}Misol 10 — Infinite query (cheksiz scroll — 2.11)
import { useInfiniteQuery } from "@tanstack/react-query";
function Feed() {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
queryKey: ["feed"],
queryFn: ({ pageParam }) => fetchFeed(pageParam),
initialPageParam: 1,
getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined, // keyingi yoki oxiri (2.11)
});
return (
<div>
{data?.pages.map((page) =>
page.items.map((post: Post) => <article key={post.id}>{post.title}</article>)
)}
{hasNextPage && (
<button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
{isFetchingNextPage ? "Yuklanmoqda..." : "Ko'proq"}
</button>
)}
</div>
);
}
// data.pages — barcha sahifalar; fetchNextPage — keyingisi (cheksiz scroll — 2.11)Misol 11 — Prefetching (hover'da — 2.12)
function ProductCard({ product }: { product: Product }) {
const queryClient = useQueryClient();
const prefetch = () => {
queryClient.prefetchQuery({
queryKey: ["product", product.id],
queryFn: () => fetch(`/api/products/${product.id}`).then((r) => r.json()),
});
};
return (
<Link to={`/products/${product.id}`} onMouseEnter={prefetch}> {/* hover'da oldindan */}
{product.name}
</Link>
);
}
// Hover'da ma'lumot fonda yuklanadi bosganda darrov ochiladi (kechikishsiz — 2.12)Misol 12 — setQueryData (to'g'ridan kesh — 2.9)
// Mutation natijasini darrov keshga qo'shish (so'rovsiz — tezroq):
const mutation = useMutation({
mutationFn: addProduct,
onSuccess: (newProduct) => {
// Invalidate o'rniga — keshga to'g'ridan qo'sh (so'rov yo'q):
queryClient.setQueryData<Product[]>(["products"], (old) => [...(old ?? []), newProduct]);
},
});
// setQueryData — server javobini darrov keshga (qayta so'rovsiz — 2.9). Ehtiyot: server bilan mos bo'lsinMisol 13 — Custom hooks (API qatlami — 11.7)
// features/products/queries.ts — barcha product query/mutation custom hook (tartibli):
export function useProducts(filters?: ProductFilters) {
return useQuery({ queryKey: productKeys.list(filters ?? {}), queryFn: () => fetchProducts(filters) });
}
export function useProduct(id: string) {
return useQuery({ queryKey: productKeys.detail(id), queryFn: () => fetchProduct(id), enabled: !!id });
}
export function useAddProduct() {
const qc = useQueryClient();
return useMutation({
mutationFn: addProduct,
onSuccess: () => qc.invalidateQueries({ queryKey: productKeys.all }),
});
}
// Komponentda: const { data } = useProducts(); — toza, qayta ishlatiladigan (11.7)Misol 14 — RTK Query vs TanStack qaror (2.12)
// SERVER STATE — har doim Query (RTK yoki TanStack):
// Redux loyiha (allaqachon RTK ishlatadi) RTK Query 12.3-bob:
// createApi + endpoints (Redux store'da kesh)
// Redux'siz / yangi loyiha TanStack Query (bu bob):
// useQuery/useMutation (mustaqil, eng keng)
// CLIENT STATE — Query EMAS:
// theme/auth/modal Context 12.1-bob yoki Zustand (12.5)
// katta murakkab Redux (12.2)
// Yakuniy: server Query (RTK/TanStack); client Context/Zustand/Redux (12.1: 2.10)
// Zamonaviy tendensiya: TanStack Query (server) + Zustand (kichik client) — yengil, kuchliMisol 15 — select (data transform — 2.13)
// select — keshdagi butun ma'lumotdan faqat kerakli qismini olish (transform):
function ProductNames() {
const { data: names } = useQuery({
queryKey: ["products"],
queryFn: fetchProducts,
select: (data) => data.map((p) => p.name), // faqat nomlar (2.13)
});
return <ul>{names?.map((n) => <li key={n}>{n}</li>)}</ul>;
}
// Kesh butun Product[] holicha saqlanadi; select faqat SHU komponent data'sini o'zgartiradi.
// Boshqa komponent select'siz to'liq ma'lumotni oladi — bitta so'rov, ko'p ko'rinish.Misol 16 — Qidiruv: AbortSignal + keepPreviousData (2.13, 2.14)
function ProductSearch() {
const [query, setQuery] = useState("");
const { data, isFetching } = useQuery({
queryKey: ["products", "search", query],
queryFn: ({ signal }) => // signal — avtomatik bekor (2.14)
fetch(`/api/products?q=${query}`, { signal }).then((r) => r.json()),
enabled: query.length > 1, // 2+ harfda qidir (2.7)
placeholderData: keepPreviousData, // eski natija ushlanadi (2.11)
});
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Qidirish..." />
{isFetching && <small>Qidirilmoqda...</small>}
<ul>{data?.map((p: Product) => <li key={p.id}>{p.name}</li>)}</ul>
</div>
);
}
// Har harfda query key o'zgaradi eski so'rov signal bilan BEKOR (race yo'q — 2.14)Misol 17 — Suspense mode (useSuspenseQuery — 2.15)
import { useSuspenseQuery } from "@tanstack/react-query";
import { ErrorBoundary } from "react-error-boundary";
import { Suspense } from "react";
function UserProfile({ id }: { id: string }) {
const { data: user } = useSuspenseQuery({ // isPending YO'Q — data doim bor (2.15)
queryKey: ["user", id],
queryFn: () => fetchUser(id),
});
return <h1>{user.name}</h1>; // user: User (undefined emas — TS ham biladi)
}
function Page({ id }: { id: string }) {
return (
<ErrorBoundary fallback={<p>Xatolik yuz berdi</p>}>
<Suspense fallback={<Spinner />}> {/* loading shu yerda 11.8-bob */}
<UserProfile id={id} />
</Suspense>
</ErrorBoundary>
);
}
// loading <Suspense>, error <ErrorBoundary> (deklarativ — komponent ichida tekshiruv yo'q)Misol 18 — refetchInterval polling (real-time'ga yaqin — 2.13)
// Jonli ma'lumot (buyurtma holati, hisoblagich) — muntazam avtomatik yangilash:
function OrderStatus({ orderId }: { orderId: string }) {
const { data } = useQuery({
queryKey: ["order", orderId],
queryFn: () => fetchOrder(orderId),
refetchInterval: 5000, // har 5s avtomatik qayta yuklash (polling — 2.13)
refetchIntervalInBackground: false, // tab fondaligida to'xtaydi (tejamli)
});
return <span>Holat: {data?.status}</span>;
}
// refetchInterval — WebSocket bo'lmaganda "real-time'ga yaqin" arzon yechim (2.13)5. To'g'ri va noto'g'ri holatlar
1) Server state
useEffect + fetch + useState (loading/error/race qo'lda — 11.5)
useQuery (hammasi avtomatik — 2.3)2) Query key
queryKey: "products" (string — noto'g'ri)
queryKey: ["products", id] (massiv — 2.6)3) Dependent query
user kelmasdan projects so'rash (xato — user undefined)
enabled: !!user?.id (user bo'lganda — 2.7)4) Kesh yangilash
mutation'dan keyin qo'lda refetch yoki sahifani yangilash
invalidateQueries (onSuccess — 2.9)5) Pagination
sahifa almashganda oq ekran (placeholderData yo'q)
keepPreviousData (eski ushlanadi — silliq — 2.11)6) Client vs server
theme/modal'ni useQuery'da (server emas)
client state Context/Zustand; server Query (2.12)7) Qidiruvda race
har harfda fetch (signal yo'q — eski javob kechikib UI'ni buzadi — 11.5)
queryFn: ({ signal }) => fetch(url, { signal }) (eski bekor — 2.14)8) Ma'lumotdan qismini olish
komponentda data'ni har render qayta hisoblash/filtrlash (keraksiz render)
select: (data) => ... (memoized transform — 2.13)6. Keng tarqalgan xatolar va yechimlari
Xato 1 — No QueryClient set
Sababi: QueryClientProvider o'ralmagan 2.2-bob. Yechimi: <QueryClientProvider client={qc}> ildizda (Misol 1).
Xato 2 — Query har render qayta ishlaydi
Sababi: queryKey/queryFn har render yangi (inline obyekt key — 2.6). Yechimi: barqaror key (primitiv/memoized); queryFn referensiyasi muhim emas (TanStack key bilan kuzatadi).
Xato 3 — Mutation'dan keyin ro'yxat eskirgan
Sababi: invalidateQueries yo'q 2.9-bob. Yechimi: onSuccess: () => qc.invalidateQueries(...) (Misol 7).
Xato 4 — Dependent query undefined xatosi
Sababi: enabled yo'q, user kelmasdan so'rov 2.7-bob. Yechimi: enabled: !!user?.id (Misol 5).
Xato 5 — Optimistic rollback ishlamaydi
Sababi: onMutate'da previous saqlanmagan yoki onError'da rollback yo'q 2.10-bob. Yechimi: onMutate (getQueryData) + onError (setQueryData previous — Misol 8).
Xato 6 — Pagination'da sahifa "sakraydi"
Sababi: keepPreviousData yo'q 2.11-bob. Yechimi: placeholderData: keepPreviousData (Misol 9).
Xato 7 — Juda ko'p so'rov (refetch tez-tez)
Sababi: staleTime: 0 (default — har mount/focus refetch — 2.4). Yechimi: staleTime oshir (kam o'zgaradigan ma'lumotga — Misol 1).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- RTK Query 12.3-bob: muqobil server-state (Redux ekotizimida); bir xil falsafa.
- Context 12.1-bob: server state — Context'da emas, Query'da (12.1: 2.10).
- Custom hooks 11.7-bob: query/mutation custom hook (useProducts — Misol 13).
- Forma 11.10-bob: mutation + RHF (submit mutate).
- Routing 11.9-bob: prefetch (hover); loader o'rniga Query (11.9: 2.13).
- Loading holatlari (11.15: 2.8): isPending/isFetching — 4 holat avtomatik.
- Suspense 11.8-bob: useSuspenseQuery (Suspense + ErrorBoundary bilan — 2.15, 11.8: 2.12).
- Qidiruv/race 11.5-bob: queryFn AbortSignal — eski so'rov avtomatik bekor 2.14-bob.
- Next.js 13.5-bob: server-side prefetch + dehydrate/HydrationBoundary (TanStack Query SSR — 2.16).
- TypeScript 11.3-bob: queryFn tipidan data tipi avtomatik 2.16-bob.
8. Eng yaxshi amaliyotlar (best practices)
- Server state TanStack Query'da (useEffect+fetch emas — 2.3).
- Query key massiv + factory (struktura, tartibli — 2.6, Misol 4).
- Custom hooklar (useProducts/useAddProduct — qayta ishlatiladigan — Misol 13).
- invalidateQueries onSuccess (kesh sync — 2.9).
- staleTime to'g'ri (kam o'zgaradiganga oshir — kam so'rov — 2.4).
- enabled dependent query'ga (oldingi natija kelguncha — 2.7).
- keepPreviousData pagination'ga (silliq — 2.11).
- Optimistic tez UX'ga (onMutate + rollback — 2.10).
- DevTools'dan foydalan (kesh debug — 2.12).
selectbilan transform (kerakli qismi — keraksiz render kam — 2.13).AbortSignalqidiruvga (eski so'rov bekor — race yo'q — 2.14).- Server Query; client Context/Zustand/Redux (2.12, 12.1: 2.10).
9. Amaliy loyiha: "TanStack Query bilan To'liq Data Qatlami"
TanStack Query'ni real ilovada — query, mutation, infinite, optimistic — mustahkamlash.
Maqsad
Mahsulot/feed ilovasi uchun to'liq TanStack Query data qatlami: ro'yxat, tafsilot, CRUD, infinite scroll, optimistic.
Talablar (requirements)
- Setup: QueryClient + Provider + DevTools (Misol 1).
- useQuery: ro'yxat va tafsilot (custom hook — Misol 2, 3, 13).
- Query key factory: markazlashgan key'lar (Misol 4, 2.6).
- Dependent query: biri ikkinchisiga bog'liq (enabled — Misol 5, 2.7).
- useMutation: add/update/delete + invalidation (Misol 7, 2.8, 2.9).
- Optimistic: like/toggle (onMutate + rollback — Misol 8, 2.10).
- Pagination yoki infinite: keepPreviousData yoki useInfiniteQuery (Misol 9, 10).
- Prefetch: hover'da tafsilot (Misol 11, 2.12).
- staleTime: kam o'zgaradigan ma'lumotga to'g'ri 2.4-bob.
- Holat: isPending/isFetching/error to'g'ri (4 holat — 2.5).
Maslahatlar (hint)
- Query'larni custom hook'ga o'ra (useProducts — qayta ishlatiladigan — Misol 13).
- Key — massiv + factory (typo/invalidation xatolaridan — Misol 4).
- Mutation'dan keyin invalidateQueries (qo'lda refetch emas — Misol 7).
- Dependent — enabled (Xato 4).
- Infinite — getNextPageParam (Misol 10).
- DevTools bilan kesh holatini kuzat (debug — 2.12).
- Client state (theme/modal) — Query'da emas (Zustand/Context — 2.12).
"Tayyor" mezonlari (acceptance criteria)
- Setup (QueryClient + Provider + DevTools).
- useQuery (ro'yxat/tafsilot — custom hook).
- Query key factory.
- Dependent query (enabled).
- CRUD mutation + invalidation.
- Optimistic update (rollback bilan).
- Pagination yoki infinite scroll.
- Prefetch (hover).
- staleTime to'g'ri (kam so'rov).
- Holatlar (isPending/isFetching/error).
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda TanStack Query'ni chuqur o'rgandik:
- TanStack Query (mustaqil server-state — 2.1); setup (QueryClient — 2.2); useQuery (queryKey/queryFn — 2.3); hayot sikli/kesh (staleTime/gcTime — 2.4); holatlar 2.5-bob.
- Query keys (factory — 2.6); dependent/parallel 2.7-bob; useMutation 2.8-bob; invalidation/cache update 2.9-bob; optimistic 2.10-bob.
- Pagination/infinite 2.11-bob; prefetching/DevTools/qaror 2.12-bob.
- Opsiyalar (select/refetch/retry/status — 2.13); cancellation (AbortSignal — 2.14); Suspense mode (useSuspenseQuery — 2.15); SSR/hydration va TypeScript 2.16-bob.
Endi siz server ma'lumotini TanStack Query bilan professional — kesh, invalidation, optimistic, infinite scroll, prefetch — boshqara olasiz, va useEffect+fetch'ning butun og'rig'idan qutulasiz. Eng muhimi — server state (Query) vs client state (Context/Zustand/Redux) ajratishini to'liq tushunasiz.
Keyingi bob — 12.5-bob: Zustand. Redux katta ilova uchun zo'r, lekin u biroz boilerplate va sozlama talab qiladi. Zustand — kichik, yengil, oddiy client-state kutubxonasi (Redux'ning minimalist muqobili): bir necha qator bilan global store yaratasiz, hech qanday Provider, boilerplate, yoki murakkab sozlama yo'q — lekin tabiiy selektor (Context cheklovini hal qiladi), middleware, va persist bilan. Zamonaviy tendensiya — TanStack Query (server) + Zustand (kichik client) — yengil, kuchli, kam kod. Bu — ko'p loyiha uchun ideal kombinatsiya.
Foydalanilgan rasmiy/ishonchli manbalar
- TanStack Query rasmiy hujjati —
useQuery,useMutation, query keys, caching (staleTime/gcTime), invalidation, optimistic updates, infinite queries,select,refetchopsiyalari,retry. - TanStack Query — Guides — "Query Cancellation" (
AbortSignal), "Dependent Queries" (enabled), "Paginated / Lagged Queries" (placeholderData: keepPreviousData), "Suspense" (useSuspenseQuery). - TkDodo blog (Dominik Dorfmeister — TanStack Query maintainer) — "Practical React Query", "React Query as a State Manager", "Effective React Query Keys", "React Query and TypeScript".
- TanStack Query — SSR & Server Components —
prefetchQuery,dehydrate,HydrationBoundary(Next.js integratsiyasi — 13-QISM). - TanStack Query v5 migration guide —
isLoadingisPending,keepPreviousDataplaceholderData,cacheTimegcTime,status/fetchStatusajratish. - React rasmiy hujjati —
<Suspense>va Error Boundary (Suspense mode konteksti — 11.8).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!