Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Kesh hayot sikli
- 2.1 Yangi va eskirgan
- 2.2 gcTime — qachon o'chiriladi
- 3. Avtomatik yangilanishlar
- 3.1 Uchta "qulay payt"
- 3.2 Oynaga qaytish
- 3.3 Davriy yangilash: refetchInterval
- 4. Kalitlar: tartib va bitta joy
- 4.1 Kalitlar daraxti va qisman moslik
- 4.2 Kalit fabrikasi
- 4.3 queryOptions — kalit va funksiya birga
- 5. So'rovni boshqarish: enabled va select
- 5.1 enabled — hozircha so'rama
- 5.2 select — keshdan kerakli qismini olish
- 6. Bir nechta so'rov
- 6.1 Parallel so'rovlar
- 6.2 Bog'liq so'rovlar
- 6.3 useQueries — so'rovlar soni oldindan noma'lum
- 7. Vazifalar qadami: kesh qoidalari va o'lchov (holat 14 davomi)
- 7.1 royxatSorovi — so'rov bitta joyda
- 7.2 E2e topgan xato: oflayn ochilgan sahifa
- 7.3 Loader — o'sha kesh
- 7.4 O'lchov: 7 qadamda nechta so'rov
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): so'rov ketadimi?
- 2-mashq (o'rta): kalit fabrikasi
- 3-mashq (qiyin): bog'liq so'rov va select
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
TanStack Query kesh: staleTime, gcTime, kalitlar va bog'liq so'rovlar
Qisqacha: Keshdagi ma'lumot ikki xil vaqt bilan yashaydi.
staleTime— qancha vaqt "yangi" hisoblanadi: shu muddatda Query serverga qayta so'ramaydi.gcTime— hech kim ishlatmay qolgandan keyin keshda qancha turadi, keyin o'chiriladi. Sukut bo'yichastaleTime: 0— ma'lumot darhol eskiradi va sahifa ochilganda, oynaga qaytilganda, internet qaytganda fonda yangilanadi.
Bu darsda
staleTimevagcTimeni farqlaysiz va ularni o'lchov bilan tanlaysiz.- Avtomatik yangilanishlarni (
refetchOnMount,refetchOnWindowFocus,refetchOnReconnect) taniysiz va kerak bo'lsa o'chirasiz. - So'rov kalitlarini (query key) bitta joyda yig'asiz: kalit fabrikasi va
queryOptions. enabled,select, bog'liq va parallel so'rovlar,useQueriesbilan ishlaysiz.vazifalarda 7 qadamdagi so'rovlar sonini 4 tadan 2 taga tushirasiz.
Oldin bilishingiz kerak: TanStack Query I: useQuery asoslari, Server state vs client state, React Router IV: data mode.
1. Nega bu kerak?
Oldingi darsda bir narsani ko'rdik: «Osh» ga qaytganda narx keshdan darhol chiqdi, lekin konsolda yana so'rov: osh bor edi. Query keshdagini ko'rsatdi va baribir serverga so'radi.
Bu yaxshimi? Vaziyatga bog'liq. «Bahor» menyusi kuniga bir marta o'zgaradi — mehmon har bosganda serverga so'rash isrof. Oshxonadagi "tayyor buyurtmalar" ekrani esa har soniyada o'zgaradi — u yerda 30 soniyalik eski ma'lumot xato.
Menyu doskasi o'xshatishini davom ettiramiz. Doskaga ikki yorliq yopishtiramiz. Birinchisi — "yangilik muddati": shu vaqtgacha yozuvga ishonsa bo'ladi. Ikkinchisi — "o'chirish muddati": bu bo'limga hech kim qaramay qo'ysa, qachon o'chiriladi. TanStack Query'da bu ikki yorliq — staleTime va gcTime.
2. Kesh hayot sikli
2.1 Yangi va eskirgan
Keshdagi har yozuv ikki holatdan birida bo'ladi:
- yangi (fresh) — ishonsa bo'ladi. Query serverga so'ramaydi, keshdagini beradi.
- eskirgan (stale) — ko'rsatiladi, lekin birinchi qulay paytda fonda yangilanadi.
staleTime — ma'lumot kelgandan keyin qancha millisekund yangi turishi. Sukut — 0: kelgan zahoti eskiradi. Shuning uchun oldingi darsda «Osh» ga qaytganda so'rov ketdi.
Sinab ko'ramiz. Menyuni yopib, qayta ochamiz (Menu olib tashlanadi va qayta qo'yiladi):
import { useState } from "react";
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchMenu() {
console.log("so'rov ketdi");
return new Promise((resolve) =>
setTimeout(() => resolve(["Osh", "Manti"]), 100),
);
}
function Menu() {
const { isPending, data } = useQuery({
queryKey: ["menu"],
queryFn: fetchMenu,
});
return <p>{isPending ? "Yuklanmoqda…" : data.join(", ")}</p>;
}
function Page() {
const [open, setOpen] = useState(true);
return (
<>
<button onClick={() => setOpen(false)}>Yopish</button>{" "}
<button onClick={() => setOpen(true)}>Ochish</button>
{open && <Menu />}
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Page />
</QueryClientProvider>
);
}Konsolda:
so'rov ketdi
so'rov ketdiEkranda:
Yopish Ochish
Osh, MantiQayta ochganda "Yuklanmoqda…" chiqmadi — menyu keshdan darhol keldi. Lekin so'rov baribir ketdi. Endi bitta qator qo'shamiz: staleTime: 30_000 (30 soniya). Raqamdagi _ — faqat o'qish uchun ajratuvchi, 30_000 va 30000 bir xil son.
import { useState } from "react";
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 30_000 } },
});
function fetchMenu() {
console.log("so'rov ketdi");
return new Promise((resolve) =>
setTimeout(() => resolve(["Osh", "Manti"]), 100),
);
}
function Menu() {
const { isPending, data } = useQuery({
queryKey: ["menu"],
queryFn: fetchMenu,
});
return <p>{isPending ? "Yuklanmoqda…" : data.join(", ")}</p>;
}
function Page() {
const [open, setOpen] = useState(true);
return (
<>
<button onClick={() => setOpen(false)}>Yopish</button>{" "}
<button onClick={() => setOpen(true)}>Ochish</button>
{open && <Menu />}
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Page />
</QueryClientProvider>
);
}Konsolda:
so'rov ketdiEkranda:
Yopish Ochish
Osh, MantiBitta so'rov. 30 soniya ichida menyuni necha marta ochsangiz ham, serverga murojaat yo'q. 30 soniyadan keyin ochsangiz — keshdagi darhol ko'rinadi va fonda yangilanadi. "Yuklanmoqda…" hech qachon qaytmaydi.
staleTime ni ikki joyda berish mumkin: QueryClient ning defaultOptions ida (hamma so'rov uchun) yoki bitta useQuery da (faqat shu so'rov uchun). Ikkinchisi birinchisidan ustun.
2.2 gcTime — qachon o'chiriladi
Endi teskari savol. Menyuni yopdik va hech kim uni ko'rsatmayapti. Kesh uni abadiy saqlaydimi? Yo'q. Yozuvni ishlatadigan komponent qolmasa, u faol emas (inactive) bo'ladi. Shu paytdan gcTime sanaladi. Muddat tugasa — yozuv keshdan o'chiriladi. Nomi "garbage collection" (axlat yig'ish) dan — Xotira boshqaruvi va garbage collection darsidagi tushuncha. Sukut — 5 daqiqa.
Ko'rsatish uchun gcTime: 300 (0.3 s) qilamiz. Yopamiz, 0.6 s kutamiz, ochamiz:
import { useState } from "react";
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 30_000, gcTime: 300 } },
});
function fetchMenu() {
console.log("so'rov ketdi");
return new Promise((resolve) =>
setTimeout(() => resolve(["Osh", "Manti"]), 200),
);
}
function Menu() {
const { isPending, data } = useQuery({
queryKey: ["menu"],
queryFn: fetchMenu,
});
return <p>{isPending ? "Yuklanmoqda…" : data.join(", ")}</p>;
}
function Page() {
const [open, setOpen] = useState(true);
return (
<>
<button onClick={() => setOpen(false)}>Yopish</button>{" "}
<button onClick={() => setOpen(true)}>Ochish</button>
{open && <Menu />}
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Page />
</QueryClientProvider>
);
}Konsolda:
so'rov ketdi
so'rov ketdiEkranda:
Yopish Ochish
Osh, MantistaleTime 30 s bo'lsa ham, ikkinchi so'rov ketdi va "Yuklanmoqda…" qaytdi. Chunki yozuv o'chib ketgan edi — "yangi" yoki "eskirgan" deyishga hech narsa qolmagan. Qoida: gcTime ni staleTime dan kichik qilmang. Aks holda "30 s yangi" va'dasi bajarilmaydi.
Butun hayot sikli bitta rasmda:
stateDiagram-v2
[*] --> Yangi: javob keldi
Yangi --> Eskirgan: staleTime o'tdi
Eskirgan --> Yangi: fonda yangilandi
Yangi --> Faolsiz: hech kim ishlatmaydi
Eskirgan --> Faolsiz: hech kim ishlatmaydi
Faolsiz --> Yangi: yana ishlatildi
Faolsiz --> [*]: gcTime o'tdi — o'chirildiRasmda bitta nozik joy bor: faolsiz yozuv yana ishlatilsa, u eskirgan bo'lsa yangilanadi, yangi bo'lsa — so'rovsiz beriladi.
| Sozlama | Sukut | Nimani hal qiladi |
|---|---|---|
staleTime |
0 |
qachon fonda qayta so'rash kerak |
gcTime |
5 daqiqa | faolsiz yozuv qachon o'chiriladi |
Tekshirib ko'ring:
staleTime: 60_000,gcTimesukutda (5 daq). Foydalanuvchi menyuni ochdi, 2 daqiqadan keyin boshqa sahifaga o'tdi, yana 1 daqiqadan keyin qaytdi. Ekranda "Yuklanmoqda…" bo'ladimi? Serverga so'rov ketadimi?
Javob
"Yuklanmoqda…" bo'lmaydi: yozuv faolsiz bo'lganiga 1 daqiqa, gcTime 5 daqiqa — u keshda. Menyu darhol ko'rinadi. So'rov esa ketadi: ma'lumot kelganiga 3 daqiqa bo'ldi, staleTime 1 daqiqa — eskirgan. Demak fonda yangilanadi.
3. Avtomatik yangilanishlar
3.1 Uchta "qulay payt"
Eskirgan ma'lumot "birinchi qulay paytda" yangilanadi dedik. Query uchta paytni kuzatadi:
| Sozlama | Qachon | Sukut |
|---|---|---|
refetchOnMount |
shu kalitni ishlatadigan komponent ekranga chiqqanda | true |
refetchOnWindowFocus |
foydalanuvchi oynaga (tabga) qaytganda | true |
refetchOnReconnect |
internet qaytganda | true |
true — "faqat eskirgan bo'lsa". false — hech qachon. "always" — eskirgan-eskirmaganidan qat'i nazar.
Uchtasi ham staleTime ga bo'ysunadi. staleTime: 30_000 bo'lsa, 30 soniya ichida oynaga necha marta qaytsangiz ham so'rov yo'q.
3.2 Oynaga qaytish
Mehmon menyuni ochdi, Telegram'ga o'tdi, 10 daqiqadan keyin qaytdi. Bu orada narx o'zgargan bo'lishi mumkin. refetchOnWindowFocus aynan shu holat uchun.
Natija oynasida tabni almashtirish qiyin. Shuning uchun Query'ning focusManager idan foydalanamiz — u "oyna faolmi?" degan savolga javob beradi. Tugmalar foydalanuvchining ketishi va qaytishini taqlid qiladi:
import {
focusManager,
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
let version = 0;
function fetchPrice() {
version += 1;
console.log("so'rov", version);
return Promise.resolve(35000 + (version - 1) * 1000);
}
function Price() {
const { data } = useQuery({
queryKey: ["price", "osh"],
queryFn: fetchPrice,
});
return (
<>
<p>Osh: {data} so'm</p>
<button onClick={() => focusManager.setFocused(false)}>
Telegram'ga o'tish
</button>{" "}
<button onClick={() => focusManager.setFocused(true)}>
Saytga qaytish
</button>
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Price />
</QueryClientProvider>
);
}Konsolda:
so'rov 1
so'rov 2Ekranda:
Osh: 36000 so'm
Telegram'ga o'tish Saytga qaytishQaytish bilan yangi narx keldi. Haqiqiy brauzerda bu tugmalar kerak emas: Query visibilitychange hodisasini o'zi tinglaydi. «Ishga tushir» dan keyin brauzerning boshqa tabiga o'tib qayting — konsolda yangi so'rov chiqadi.
DevTools bilan ishlaganda bu xulq hayron qoldiradi: konsoldan sahifaga qaytsangiz ham Network panelida yangi so'rovlar. Bu xato emas — eskirgan ma'lumot yangilanyapti. Kerak bo'lmasa — staleTime ni oshiring yoki refetchOnWindowFocus: false.
3.3 Davriy yangilash: refetchInterval
To'rtinchi usul — vaqt bo'yicha. refetchInterval: 5000 — har 5 soniyada so'rov, ma'lumot yangi bo'lsa ham. Oshxonadagi "tayyor buyurtmalar" ekrani uchun oddiy yechim. Bu usulning nomi — polling (so'rab turish). Uning kamchiligi va muqobillari (WebSocket, SSE) — Real-time darsida.
Maslahat:
staleTimeni qanday tanlash kerak? Savol bering: "foydalanuvchi necha soniyalik eski ma'lumotni ko'rsa, zarar bo'ladi?" Menyu — daqiqalar, hatto soat. Bank balansi — 0.vazifalar— 30 s (o'lchov bilan, «Vazifalar qadami» bo'limida). Bilmasangiz, sukut (0) bilan boshlang va Network panelida ortiqcha so'rovlarni ko'rganda oshiring.
4. Kalitlar: tartib va bitta joy
4.1 Kalitlar daraxti va qisman moslik
So'rov kalitlari (query key, 13-darsdagi queryKey) oddiy massiv bo'lgani bilan, ular daraxt kabi ishlaydi. ["dishes"] — hamma taomlar haqidagi so'rovlarning ildizi. ["dishes", "list", "issiq"] va ["dishes", "detail", "osh"] — uning shoxlari.
Bu nega muhim: ko'p metodlar kalitni boshidan solishtiradi. invalidateQueries({ queryKey: ["dishes"] }) — "dishes bilan boshlanadigan hamma so'rov eskirdi". Ro'yxat ham, har bir taom ham yangilanadi. Invalidatsiya (invalidation) — keshdagi yozuvni majburan "eskirgan" deb belgilash. Ekrandagilari darhol qayta so'raladi.
4.2 Kalit fabrikasi
Kalitni har komponentda qo'lda yozsangiz, bir kun ["dish", slug] va boshqa joyda ["dishes", slug] paydo bo'ladi. Ular bir-birini bilmaydi va invalidatsiya ishlamaydi.
Davosi — kalit fabrikasi (query key factory): hamma kalit bitta obyektda, funksiya sifatida:
import {
QueryClient,
QueryClientProvider,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 60_000 } },
});
// Hamma kalit — bitta joyda
const dishKeys = {
all: ["dishes"],
list: (category) => [...dishKeys.all, "list", category],
detail: (slug) => [...dishKeys.all, "detail", slug],
};
function fakeServer(key) {
console.log("so'rov:", JSON.stringify(key));
return Promise.resolve(key.at(-1));
}
function List() {
const key = dishKeys.list("issiq");
const { data } = useQuery({
queryKey: key,
queryFn: () => fakeServer(key),
});
return <p>Ro'yxat: {data}</p>;
}
function Detail() {
const key = dishKeys.detail("osh");
const { data } = useQuery({
queryKey: key,
queryFn: () => fakeServer(key),
});
return <p>Taom: {data}</p>;
}
function Admin() {
const client = useQueryClient();
return (
<button
onClick={() =>
client.invalidateQueries({ queryKey: dishKeys.all })
}
>
Narxlar o'zgardi
</button>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<List />
<Detail />
<Admin />
</QueryClientProvider>
);
}Konsolda:
so'rov: ["dishes","list","issiq"]
so'rov: ["dishes","detail","osh"]
so'rov: ["dishes","list","issiq"]
so'rov: ["dishes","detail","osh"]Ekranda:
Ro'yxat: issiq
Taom: osh
Narxlar o'zgardistaleTime bir daqiqa edi — o'z-o'zidan yangilanish bo'lmasdi. Lekin bitta invalidateQueries({ queryKey: dishKeys.all }) ikkala so'rovni ham yangiladi: ikkalasining kaliti "dishes" bilan boshlanadi. Faqat ro'yxatlarni yangilash kerak bo'lsa — ["dishes", "list"].
4.3 queryOptions — kalit va funksiya birga
Fabrika kalitni bir joyga yig'di. Lekin queryFn hali har komponentda alohida. Kalit va funksiya doim juft yuradi — ["dishes", "detail", "osh"] uchun boshqa funksiya yozish xato.
queryOptions — so'rov sozlamalarini (kalit, funksiya, staleTime …) bitta obyektga yig'adigan yordamchi. JavaScript'da u obyektni o'zgartirmasdan qaytaradi. Uning kuchi — TypeScript'da: kalitga ma'lumot turi "yopishadi".
import {
QueryClient,
QueryClientProvider,
queryOptions,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchDish(slug) {
console.log("so'rov:", slug);
return Promise.resolve({ slug, price: 35000 });
}
// Kalit + funksiya + sozlamalar — bitta joyda
function dishQuery(slug) {
return queryOptions({
queryKey: ["dishes", "detail", slug],
queryFn: () => fetchDish(slug),
staleTime: 60_000,
});
}
function Dish() {
const { data } = useQuery(dishQuery("osh"));
return <p>Narx: {data?.price}</p>;
}
function Cart() {
const client = useQueryClient();
// Keshdan o'qish — o'sha kalit, xato qilib bo'lmaydi
const cached = client.getQueryData(dishQuery("osh").queryKey);
return <p>Savatda: {cached ? cached.slug : "hali yo'q"}</p>;
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Dish />
<Cart />
</QueryClientProvider>
);
}Konsolda:
so'rov: oshEkranda:
Narx: 35000
Savatda: hali yo'quseQuery(dishQuery("osh")) — obyektni to'g'ridan-to'g'ri beramiz. getQueryData(kalit) — keshdagi qiymatni so'rovsiz oladi. Cart chizilganda javob hali kelmagan edi, shuning uchun "hali yo'q". Va Cart kesh o'zgarganini kuzatmaydi — getQueryData bir martalik o'qish, obuna emas. Kuzatish kerak bo'lsa — useQuery.
TypeScript'da farq ko'rinadi. dishQuery ning queryFn i Promise<Dish> qaytarsa, getQueryData(dishQuery("osh").queryKey) turi — Dish | undefined. queryOptions siz — unknown. Kanonda shu imkoniyat ishlatiladi. Tekshiramiz:
import { QueryClient, queryOptions } from "@tanstack/react-query";
type Dish = { slug: string; price: number };
declare function fetchDish(slug: string): Promise<Dish>;
const client = new QueryClient();
const dishQuery = (slug: string) =>
queryOptions({
queryKey: ["dishes", "detail", slug],
queryFn: () => fetchDish(slug),
});
const a = client.getQueryData(dishQuery("osh").queryKey);
const b = client.getQueryData(["dishes", "detail", "osh"]);
console.log(a.price, b.price);Xato:
TS18048: 'a' is possibly 'undefined'.
TS18046: 'b' is of type 'unknown'.Kompilyator a ni taniydi — u Dish | undefined, faqat "keshda yo'q bo'lishi mumkin" deb ogohlantiradi (a?.price bilan hal bo'ladi). b haqida esa hech narsa bilmaydi — unknown, price maydoni bor-yo'qligini ham.
queryOptions qaytargan obyekt hamma joyda ishlaydi: useQuery, useSuspenseQuery, prefetchQuery, fetchQuery, router loader. Ularni TanStack Query V darsida ko'ramiz. vazifalar ning royxatSorovi() — aynan shunday funksiya.
Tekshirib ko'ring: Kalitlar:
["dishes", "list", "issiq"],["dishes", "detail", "osh"],["drinks", "list"].invalidateQueries({ queryKey: ["dishes", "list"] })qaysilarini yangilaydi?
Javob
Faqat birinchisini. Solishtirish kalit boshidan: ["dishes", "list", "issiq"] — "dishes", "list" bilan boshlanadi. ["dishes", "detail", "osh"] ning ikkinchi elementi "detail", mos emas. ["drinks", "list"] — birinchi elementdayoq farq.
5. So'rovni boshqarish: enabled va select
5.1 enabled — hozircha so'rama
Qidiruv maydoni: mehmon "o" yozdi. Bitta harf bo'yicha qidirish ma'nosiz — yuzta natija. Ikki harfdan keyin qidirish kerak. enabled: false — so'rov ketmaydi, Query kutib turadi:
import { useState } from "react";
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
const MENU = ["Osh", "Oshqovoq somsa", "Manti", "Lag'mon"];
function searchMenu(text) {
console.log("qidiruv:", text);
return new Promise((resolve) =>
setTimeout(
() =>
resolve(
MENU.filter((d) =>
d.toLowerCase().startsWith(text.toLowerCase()),
),
),
200,
),
);
}
function Search() {
const [text, setText] = useState("");
const query = useQuery({
queryKey: ["search", text],
queryFn: () => searchMenu(text),
enabled: text.length >= 2,
});
console.log(query.status, query.fetchStatus);
let result;
if (text.length < 2) result = "Kamida 2 harf yozing";
else if (query.isLoading) result = "Qidirilmoqda…";
else result = query.data.join(", ");
return (
<>
<input
aria-label="Qidiruv"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<p>{result}</p>
</>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Search />
</QueryClientProvider>
);
}Konsolda:
pending idle
pending idle
pending fetching
qidiruv: osh
success idleEkranda ("osh" yozilgach):
Osh, Oshqovoq somsaKonsolni qatorma-qator o'qiymiz. Birinchi qator — maydon bo'sh, ikkinchisi — "o" yozildi. Ikkalasida ham so'rov yo'q. "osh" yozilganda enabled rost bo'ldi va so'rov ketdi. Oraliq nuqtada ekranda "Qidirilmoqda…" edi, keyin natija.
Birinchi ikki qatorga yana qarang: pending idle. So'rov o'chirilgan paytda status — "pending" (ma'lumot yo'q), fetchStatus — "idle" (so'rov ketmayapti). Bu tuzoq: isPending ga qarab "Yuklanmoqda…" ko'rsatsangiz, foydalanuvchi bitta harf yozgach abadiy aylanuvchi belgini ko'radi. Shuning uchun kodda isLoading — u isPending && isFetching, ya'ni "ma'lumot yo'q va so'rov ketyapti".
enabled ning ikki ko'p uchraydigan ishi:
- foydalanuvchi kiritishi kutilyapti — qidiruv, filtr, forma;
- boshqa so'rov kutilyapti — bog'liq so'rovlar (pastda).
TypeScript'da enabled o'rniga skipToken ham ishlatiladi: queryFn: text.length >= 2 ? () => searchMenu(text) : skipToken. Afzalligi — funksiya ichida text turini toraytirish oson.
5.2 select — keshdan kerakli qismini olish
Bosh sahifada "Bugun 4 ta taom" yozuvi kerak. Butun menyu keshda bor. Alohida "sanash" so'rovi yozish shart emas — select keshdagi ma'lumotni komponentga berishdan oldin o'zgartiradi:
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchMenu() {
console.log("so'rov ketdi");
return Promise.resolve([
{ name: "Osh", price: 35000 },
{ name: "Lag'mon", price: 28000 },
{ name: "Manti", price: 30000 },
{ name: "Ko'k choy", price: 5000 },
]);
}
const menuQuery = { queryKey: ["menu"], queryFn: fetchMenu };
function Count() {
const { data } = useQuery({
...menuQuery,
select: (menu) => menu.length,
});
return <p>Bugun {data} ta taom</p>;
}
function Cheapest() {
const { data } = useQuery({
...menuQuery,
select: (menu) =>
menu.reduce((a, b) => (b.price < a.price ? b : a)).name,
});
return <p>Eng arzoni: {data}</p>;
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Count />
<Cheapest />
</QueryClientProvider>
);
}Konsolda:
so'rov ketdiEkranda:
Bugun 4 ta taom
Eng arzoni: Ko'k choyKesh bitta, so'rov bitta, har komponent o'z "ko'rinishini" oladi. Keshda esa asl menyu qoladi — select uni o'zgartirmaydi.
Ikkinchi foyda — kamroq render. Count faqat select natijasi (son) o'zgarganda qayta chiziladi. Menyuda narx o'zgarsa, son o'sha — Count jim turadi. Bu memo, useMemo, useCallback darsidagi g'oyaning Query'dagi ko'rinishi.
Diqqat:
selectga har render'da yangi funksiya bersangiz (select: (m) => …komponent ichida), Query uni har safar qayta ishlatadi. Oddiy hisobda (length) bu sezilmaydi. Og'ir hisobda funksiyani komponentdan tashqariga chiqaring yokiuseCallbackbilan o'rang.
6. Bir nechta so'rov
6.1 Parallel so'rovlar
Bitta komponentda ikkita useQuery — ular parallel ketadi, biri ikkinchisini kutmaydi:
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
const start = Date.now();
function fakeServer(name, value) {
console.log("boshlandi:", name);
return new Promise((resolve) =>
setTimeout(() => resolve(value), 200),
);
}
function Dashboard() {
const menu = useQuery({
queryKey: ["menu"],
queryFn: () => fakeServer("menyu", 4),
});
const orders = useQuery({
queryKey: ["orders"],
queryFn: () => fakeServer("buyurtmalar", 12),
});
if (menu.isPending || orders.isPending) {
return <p>Yuklanmoqda…</p>;
}
const fast = Date.now() - start < 350;
return (
<p>
{menu.data} ta taom, {orders.data} ta buyurtma
{fast ? " (parallel)" : " (ketma-ket)"}
</p>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Dashboard />
</QueryClientProvider>
);
}Konsolda:
boshlandi: menyu
boshlandi: buyurtmalarEkranda:
4 ta taom, 12 ta buyurtma (parallel)Har biri 200 ms. Ketma-ket bo'lsa — 400 ms, parallel — 200 ms. Ketma-ket va parallel bajarish darsidagi Promise.all ning React'dagi o'xshashi.
6.2 Bog'liq so'rovlar
Ba'zan ikkinchi so'rov birinchisining natijasiga muhtoj. Mehmonni telefon raqami bo'yicha topamiz, keyin uning buyurtmalarini olamiz — buyurtmalar uchun mehmonning id si kerak. Bu bog'liq so'rov (dependent query): ikkinchisi enabled bilan birinchisini kutadi:
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function fetchGuest(phone) {
console.log("mehmon so'raldi:", phone);
return new Promise((resolve) =>
setTimeout(() => resolve({ id: 7, name: "Malika" }), 200),
);
}
function fetchOrders(guestId) {
console.log("buyurtmalar so'raldi:", guestId);
return new Promise((resolve) =>
setTimeout(() => resolve(["Osh", "Ko'k choy"]), 200),
);
}
function GuestOrders({ phone }) {
const guest = useQuery({
queryKey: ["guest", phone],
queryFn: () => fetchGuest(phone),
});
const guestId = guest.data?.id;
const orders = useQuery({
queryKey: ["orders", guestId],
queryFn: () => fetchOrders(guestId),
enabled: guestId !== undefined,
});
if (guest.isPending) return <p>Mehmon qidirilmoqda…</p>;
if (orders.isPending) return <p>Buyurtmalar yuklanmoqda…</p>;
return (
<p>
{guest.data.name}: {orders.data.join(", ")}
</p>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<GuestOrders phone="+998 90 000 00 00" />
</QueryClientProvider>
);
}Konsolda:
mehmon so'raldi: +998 90 000 00 00
buyurtmalar so'raldi: 7Ekranda:
Malika: Osh, Ko'k choyIshlaydi, lekin narxi bor: 200 + 200 = 400 ms. Bu sharshara (waterfall) — so'rovlar zinapoya kabi birin-ketin. Har bog'liq so'rov kutishni uzaytiradi. Iloji bo'lsa, server bilan kelishing: bitta so'rov ikkalasini qaytarsin (/guests/by-phone/…?include=orders). Iloji bo'lmasa — bog'liq so'rov to'g'ri yechim, faqat uni ongli ishlating.
6.3 useQueries — so'rovlar soni oldindan noma'lum
Ofitsiant uchta stolga xizmat qiladi: 2, 4 va 7. Ertaga — beshta. Har stolga useQuery yozib bo'lmaydi: Hooklar qoidalari — hook siklda chaqirilmaydi. useQueries — massivdagi har element uchun bitta so'rov:
import {
QueryClient,
QueryClientProvider,
useQueries,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
const TOTALS = { 2: 63000, 4: 40000, 7: 30000 };
function fetchTotal(table) {
console.log("stol:", table);
return new Promise((resolve) =>
setTimeout(() => resolve(TOTALS[table]), 200),
);
}
function Waiter({ tables }) {
const result = useQueries({
queries: tables.map((table) => ({
queryKey: ["total", table],
queryFn: () => fetchTotal(table),
})),
combine: (results) => ({
pending: results.some((r) => r.isPending),
sum: results.reduce((s, r) => s + (r.data ?? 0), 0),
}),
});
if (result.pending) return <p>Hisoblanmoqda…</p>;
return (
<p>
{tables.length} ta stol, jami: {result.sum} so'm
</p>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Waiter tables={[2, 4, 7]} />
</QueryClientProvider>
);
}Konsolda:
stol: 2
stol: 4
stol: 7Ekranda:
3 ta stol, jami: 133000 so'mqueries — har biri oddiy useQuery sozlamasi. Uchala so'rov parallel. combine — natijalar massivini bitta qiymatga yig'adi (bu yerda — "kutilyaptimi?" va yig'indi). combine siz useQueries natijalar massivini qaytaradi. Har stolning kaliti alohida (["total", 4]), shuning uchun boshqa komponent faqat 4-stolni so'rasa — keshdan oladi.
Tekshirib ko'ring: Bog'liq so'rovlar misolida
enabled: guestId !== undefinedni olib tashlasak nima bo'ladi?
Javob
Ikkinchi so'rov darhol, mehmon topilmasdan ketadi: fetchOrders(undefined). Kalit — ["orders", undefined]. Haqiqiy serverda bu /orders?guest=undefined kabi noto'g'ri so'rov va xato bo'lardi. Mehmon topilgach kalit ["orders", 7] ga o'zgaradi va yana so'rov ketadi. Natijada bitta keraksiz, xato so'rov.
7. Vazifalar qadami: kesh qoidalari va o'lchov (holat 14 davomi)
7.1 royxatSorovi — so'rov bitta joyda
Oldingi darsda useVazifalar ichida royxatSorovi(mijoz) ni ko'rdik. Mana u (kanon, izohlar bo'lingan):
/**
* Ro'yxat so'rovi — bitta joyda (hook, loader, prefetch shuni
* oladi). initialData — localStorage'dagi nusxa: sahifa birinchi
* kadrdayoq ro'yxat bilan (CLS 0, 13/#30), initialDataUpdatedAt
* 0 — u "eski": Query darhol serverdan yangisini so'raydi
*/
export function royxatSorovi(mijoz: QueryClient = sorovMijozi) {
return queryOptions({
queryKey: royxatKaliti,
queryFn: ({ signal }) => royxatniYukla(mijoz, signal),
initialData: () => yukla() ?? boshlangichVazifalar,
initialDataUpdatedAt: 0,
// Internet qaytdi — eskirgan-eskirmaganidan qat'i nazar
// solishtirish (v8 dagi "online" hodisasi)
refetchOnReconnect: "always",
});
}Bugungi darsdan to'rt narsa:
queryOptions— kalit va funksiya juft.useVazifalarham, vazifa sahifasining loader'i ham shuni oladi.initialData— so'rovgacha keshga qo'yiladigan boshlang'ich qiymat. Bu yerda —localStoragedagi nusxa. Natija: birinchi kadrdayoq ro'yxat, "Yuklanmoqda…" yo'q (13-qismdagi CLS 0 o'lchovi saqlanadi). Funksiya ko'rinishida — faqat kerak bo'lganda bir marta o'qiladi.initialDataUpdatedAt: 0— "bu ma'lumot 1970-yil 1-yanvarda kelgan". Ya'ni u albatta eskirgan va Query darhol serverdan yangisini so'raydi. Bu qatorsizstaleTime30 s bo'lgani uchun localStorage'dagi nusxa 30 s "yangi" hisoblanardi — server bilan solishtirish kechikardi.refetchOnReconnect: "always"— internet qaytganda, ma'lumot yangi bo'lsa ham. 17-qismdagionlinehodisasi shu.
7.2 E2e topgan xato: oflayn ochilgan sahifa
Brauzer tekshiruvi (e2e) bitta haqiqiy xatoni topdi. Sahifa internetsiz ochilsa (Service Worker keshidan), so'rov yiqilib "xato" bo'lardi. Internet qaytganda esa qayta so'ralmasdi. Sababi — Query'ning onlineManager i sahifa ochilganda internetni bor deb hisoblaydi va faqat online/offline hodisalarini tinglaydi. Sahifa oflayn ochilsa, offline hodisasi bo'lmaydi — Query bilmaydi. Tuzatish — bitta qator (kanon):
// Query v5 sahifa ochilganda internetni BOR deb hisoblaydi va
// faqat online/offline hodisalarini tinglaydi. Sahifa internetsiz
// ochilsa (SW keshidan) — so'rov yiqilib "xato" bo'lardi va
// internet qaytganda "online" hodisasi holatni o'zgartirmagani
// uchun qayta so'ralmasdi (e2e topdi: 13/#22 "onlayn: sinxron
// qaytdi"). Boshlang'ich holat — brauzerdan: oflayn bo'lsa
// so'rov KUTADI va internet qaytishi bilan ketadi
onlineManager.setOnline(navigator.onLine);Oldingi darsdagi paused holati endi to'g'ri ishlaydi: oflayn ochilgan sahifada so'rov kutadi, internet qaytishi bilan ketadi.
7.3 Loader — o'sha kesh
Vazifa sahifasining loader'i (data mode darsida yozgan edik) ham endi Query keshidan oladi. fetchQuery — useQuery ning komponentsiz ko'rinishi: kesh yangi bo'lsa — so'rovsiz qaytaradi, eskirgan bo'lsa — serverdan oladi va keshga yozadi (kanon):
+// 19/#14: ro'yxat — Query keshidan (ro'yxat sahifasi bilan
+// BITTA kesh). fetchQuery: kesh yangi (30 s) bo'lsa —
+// so'rovsiz, eskirgan bo'lsa — serverdan. Server javob bermasa
+// (oflayn, xato) — keshdagi nusxa. Bekor qilingan so'rov —
+// qayta tashlanadi (eskirgan navigatsiya)
async function royxatniOl(signal: AbortSignal): Promise<Vazifalar> {
+ const kesh = (): Vazifalar =>
+ sorovMijozi.getQueryData<Vazifalar>(royxatKaliti) ??
+ yukla() ??
+ boshlangichVazifalar;
if (!navigator.onLine) {
- return keshdagi();
+ return kesh();
}
try {
- const javob = await api.royxat({ signal });
- return javob.versiya === 0 ? keshdagi() : javob.vazifalar;
+ return await sorovMijozi.fetchQuery(royxatSorovi());Loader komponent emas, hook chaqira olmaydi. Shuning uchun u sorovMijozi ni to'g'ridan-to'g'ri import qiladi — QueryClient modul darajasida yaratilgani shu uchun qulay. Testda esa har testdan oldin sorovMijozi.clear() — kesh tozalanadi, testlar bir-birining ma'lumotini ko'rmaydi.
7.4 O'lchov: 7 qadamda nechta so'rov
Server state vs client state darsidagi o'lchov skripti (olchov/sorovlar.mjs, Chrome 155) o'sha 7 qadamni uch variantda bajardi: Query'gacha (o'sha darsdagi holat), Query + staleTime: 0 (sukut) va Query + staleTime: 30_000 (kanon):
| Qadam | Query'gacha | staleTime 0 |
30 s |
|---|---|---|---|
| 1. ro'yxat ochildi | GET | GET | GET |
| 2. → Statistika | — | GET | — |
| 3. → Ro'yxat | GET | GET | — |
| 4. → «Batafsil» | GET | GET | — |
| 5. Orqaga → Ro'yxat | GET | GET | — |
| 6. serverda +1; → Statistika | — (3) | GET (4) | — (3) |
| 7. Statistika, F5 | — (3) | GET (4) | GET (4) |
Qavsda — statistikadagi "Jami". Serverda 6-qadamdan keyin 4 ta vazifa bor.
- Query'gachastatistika eskiradi: 34 ta
- Query, staleTime 0har o'tishda7 ta
- Query, staleTime 30 s (kanon)F5 dan keyin — 42 ta
Manba: sinov/19/natijalar/11-sorovlar-oldin.txt, 14-sorovlar-stale0.txt, 14-sorovlar-keyin.txt (Chrome 155.0.8059.40)
Uch variantni solishtiramiz:
- Query'gacha — 4 ta so'rov va xato: statistika hatto F5 dan keyin ham 3 ko'rsatadi.
staleTime: 0— xato yo'q, boshqa qurilmadagi +1 darhol ko'rinadi (6-qadam). Narxi — har o'tishda so'rov, 7 tadan 7.- 30 s (kanon) — 2 ta so'rov. Narxi — boshqa qurilmadagi o'zgarish 30 s gacha kechikishi mumkin (6-qadam: 3). F5 yoki 30 soniyadan keyin — to'g'ri raqam.
Kurs 30 s ni tanladi: vazifalar — bitta odamning ro'yxati, boshqa qurilmadan yarim daqiqalik kechikish zararsiz. Bir nechta odam bir ro'yxatda ishlasa — staleTime kamroq yoki real-time (Real-time darsida kechikishni 254 ms ga tushiramiz).
Brauzer tekshiruvlari (holat 14) — o'lchov xulosasini doimiy tekshiruvga aylantirdi:
--- 19/#13–#14 server holati (TanStack Query)
✅ ochilish: bitta GET: ["GET"]
✅ statistika → ro'yxat → vazifa (30 s ichida): serverga so'rov yo'q (kesh): []
✅ statistika (sahifa yangilandi): keshdan darhol, keyin serverdan — 4: ["4","3","1 (25%)"]
✅ server holati: CSP buzilishi yo'q: []Commit hali yo'q — yozish (useMutation) keyingi darsda.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Network'da tabga qaytganda so'rovlar | staleTime: 0 + refetchOnWindowFocus |
staleTime ni oshiring |
staleTime katta, lekin "Yuklanmoqda…" qaytadi |
gcTime < staleTime, yozuv o'chdi |
gcTime ≥ staleTime |
invalidateQueries hech narsani yangilamadi |
kalit boshqacha yozilgan ("dish" / "dishes") |
kalit fabrikasi yoki queryOptions |
enabled: false da abadiy "Yuklanmoqda…" |
isPending ishlatilgan |
isLoading (isPending && isFetching) |
getQueryData dan keyin ekran yangilanmaydi |
bir martalik o'qish, obuna emas | useQuery (yoki select) |
ikkinchi so'rov undefined bilan ketdi |
bog'liq so'rovda enabled yo'q |
enabled: id !== undefined |
useQuery siklda — React xatosi |
hooklar qoidasi | useQueries |
| oflayn ochilgan sahifa internet qaytganda yangilanmaydi | onlineManager boshlang'ichda "bor" |
onlineManager.setOnline(navigator.onLine) |
Oxirgisi — umumiy tuzoq emas, PWA va Service Worker'li ilovalarga xos. Lekin aynan shunday xatolar foydalanuvchida bir marta uchrab, "sayt ishlamayapti" degan taassurot qoldiradi.
9. Mashqlar
Mashqlar kurs/mashqlar/19/14-kesh/ da.
1-mashq (oson): so'rov ketadimi?
staleTime: 10_000, gcTime: 60_000. Ma'lumot 0-soniyada keldi. Har holat uchun yozing: so'rov ketadimi ("ha"/"yo'q") va "Yuklanmoqda…" bo'ladimi.
- 5-soniyada boshqa komponent shu kalitni ochdi — so'rov:
- 15-soniyada foydalanuvchi tabga qaytdi — so'rov:
- Komponent 20-soniyada yopildi, 50-soniyada qayta ochildi — "Yuklanmoqda…":
- Komponent 20-soniyada yopildi, 90-soniyada qayta ochildi — "Yuklanmoqda…":
Yechim
- Yo'q — 5 < 10, ma'lumot yangi.
- Ha — 15 > 10, eskirgan,
refetchOnWindowFocusfonda yangilaydi. Ekranda eski ma'lumot qoladi. - Yo'q — faolsiz bo'lganiga 30 s,
gcTime60 s, yozuv keshda. Darhol ko'rinadi (va eskirgani uchun fonda yangilanadi). - Ha — faolsiz bo'lganiga 70 s > 60 s, yozuv o'chgan. Boshidan yuklanadi.
2-mashq (o'rta): kalit fabrikasi
Kod ishlaydi, lekin kalitlar qo'lda yozilgan va «Yangilash» faqat ro'yxatni yangilaydi — taom sahifasi eski qoladi. orderKeys fabrikasini yozing (all, list(), detail(id)), uchala joyda ishlating va «Yangilash» orderKeys.all ni invalidatsiya qilsin.
import {
QueryClient,
QueryClientProvider,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 60_000 } },
});
function fakeServer(key) {
console.log("so'rov:", key.join("/"));
return Promise.resolve(key.length);
}
function List() {
const key = ["orders", "list"];
const { data } = useQuery({
queryKey: key,
queryFn: () => fakeServer(key),
});
return <p>Ro'yxat: {data}</p>;
}
function Detail() {
const key = ["order", 12];
const { data } = useQuery({
queryKey: key,
queryFn: () => fakeServer(key),
});
return <p>Buyurtma: {data}</p>;
}
function Refresh() {
const client = useQueryClient();
return (
<button
onClick={() =>
client.invalidateQueries({ queryKey: ["orders", "list"] })
}
>
Yangilash
</button>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<List />
<Detail />
<Refresh />
</QueryClientProvider>
);
}Konsolda:
so'rov: orders/list
so'rov: order/12
so'rov: orders/listYechim
import {
QueryClient,
QueryClientProvider,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 60_000 } },
});
const orderKeys = {
all: ["orders"],
list: () => [...orderKeys.all, "list"],
detail: (id) => [...orderKeys.all, "detail", id],
};
function fakeServer(key) {
console.log("so'rov:", key.join("/"));
return Promise.resolve(key.length);
}
function List() {
const key = orderKeys.list();
const { data } = useQuery({
queryKey: key,
queryFn: () => fakeServer(key),
});
return <p>Ro'yxat: {data}</p>;
}
function Detail() {
const key = orderKeys.detail(12);
const { data } = useQuery({
queryKey: key,
queryFn: () => fakeServer(key),
});
return <p>Buyurtma: {data}</p>;
}
function Refresh() {
const client = useQueryClient();
return (
<button
onClick={() =>
client.invalidateQueries({ queryKey: orderKeys.all })
}
>
Yangilash
</button>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<List />
<Detail />
<Refresh />
</QueryClientProvider>
);
}Konsolda:
so'rov: orders/list
so'rov: orders/detail/12
so'rov: orders/list
so'rov: orders/detail/12Asl kodda ikki xato bor edi. Birinchisi — ["order", 12] ning boshi "order", ro'yxatniki "orders" — bitta harf farq va ular boshqa "daraxt"da. Ikkinchisi — invalidatsiya faqat ro'yxat kalitiga. Fabrika ikkalasini bir yo'la tuzatdi: hamma kalit orderKeys.all dan o'sadi.
3-mashq (qiyin): bog'liq so'rov va select
«Bahor» ofitsianti stol raqamini biladi. Kerak:
fetchTable(4)→{ table: 4, orderId: 31 }(stoldagi joriy buyurtma raqami).fetchOrder(31)→[{ name: "Osh", price: 35000 }, { name: "Ko'k choy", price: 5000 }]— faqat 1-so'rov tugagach (enabled).- Buyurtma so'rovida
selectbilan jami summa hisoblansin — komponent faqat sonni oladi. - Ekranda: "4-stol: 40000 so'm". Yuklanayotganda — "Yuklanmoqda…".
Ikkala soxta server 200 ms kutsin. Ishora: «Bog'liq so'rovlar» bo'limi va reduce (reduce).
Yechim
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
function delay(value) {
return new Promise((resolve) =>
setTimeout(() => resolve(value), 200),
);
}
function fetchTable(table) {
console.log("stol:", table);
return delay({ table, orderId: 31 });
}
function fetchOrder(orderId) {
console.log("buyurtma:", orderId);
return delay([
{ name: "Osh", price: 35000 },
{ name: "Ko'k choy", price: 5000 },
]);
}
const sumPrices = (items) =>
items.reduce((sum, item) => sum + item.price, 0);
function TableTotal({ table }) {
const tableQuery = useQuery({
queryKey: ["table", table],
queryFn: () => fetchTable(table),
});
const orderId = tableQuery.data?.orderId;
const total = useQuery({
queryKey: ["order", orderId],
queryFn: () => fetchOrder(orderId),
enabled: orderId !== undefined,
select: sumPrices,
});
if (tableQuery.isPending || total.isPending) {
return <p>Yuklanmoqda…</p>;
}
return (
<p>
{table}-stol: {total.data} so'm
</p>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<TableTotal table={4} />
</QueryClientProvider>
);
}Konsolda:
stol: 4
buyurtma: 31Ekranda:
4-stol: 40000 so'msumPrices komponentdan tashqarida — har render'da yangi funksiya yaratilmaydi («select» bo'limidagi ogohlantirish). Keshda esa buyurtmaning to'liq ro'yxati qoladi: boshqa komponent taomlar nomini ko'rsatishi kerak bo'lsa, so'rovsiz oladi.
4-mashq: Vazifalar qadami
refactor/tanstack-query branch'ida davom eting: royxatSorovi (queryOptions, initialData, initialDataUpdatedAt: 0, refetchOnReconnect: "always"), onlineManager.setOnline(navigator.onLine), loader'dagi fetchQuery, VazifaSahifasi.test.tsx dagi sorovMijozi.clear().
Yechim
Tekshiruv ro'yxati:
npm run dev, DevTools Network (Fetch/XHR filtri). Ro'yxat → Statistika → Ro'yxat → «Batafsil» → Orqaga: 30 s ichida faqat birinchi GET.- 30 soniya kuting va tabga qayting — bitta GET (eskirgan,
refetchOnWindowFocus). - Network → Offline, sahifani yangilang: ro'yxat localStorage'dan, "oflayn". Online qiling — so'rov o'zi ketadi (
onlineManagerqatorisiz ketmasdi). npm test— VazifaSahifasi testlarisorovMijozi.clear()siz bir-biriga ta'sir qiladi.
10. Real ishda
staleTime— eng ko'p so'raladigan sozlama. Ko'p jamoalardefaultOptionsda 30–60 s qo'yadi va faqat "jonli" ma'lumotda kamaytiradi. Sukutdagi 0 — xavfsiz, lekin "nega shuncha so'rov?" degan savolga sabab.- Kalit fabrikasi — katta loyihalarda odatiy naqsh:
api/dishes/keys.ts. TanStack jamoasi ham buni tavsiya qiladi. Hozirgi uslub —queryOptionsfunksiyalari (dishQueries.detail(slug)), kalit va funksiya birga. select— "bitta API javobi, ko'p ko'rinish" holatida: ro'yxat, son, filtrlangan qism. Alohida endpoint yozdirishdan oldinselectbilan yetishini tekshiring.- Sharshara — sekin sahifalarning eng ko'p uchraydigan sababi. Lighthouse va Network panelidagi "zinapoya" — shu belgi.
- Intervyu savollari: "
staleTimevagcTimefarqi?", "nega tabga qaytganda so'rov ketadi?", "invalidateQueriesqanday ishlaydi?", "bog'liq so'rov va uning narxi?".
Xulosa
staleTime— ma'lumot qancha yangi turadi (sukut 0).gcTime— faolsiz yozuv qancha saqlanadi (sukut 5 daq).gcTime≥staleTime.- Eskirgan ma'lumot uch paytda fonda yangilanadi: komponent chiqqanda, oynaga qaytilganda, internet qaytganda. Ekrandagi ma'lumot yo'qolmaydi.
- Kalitlar — daraxt:
invalidateQueriesboshidan solishtiradi. Kalitlarni fabrikaga yokiqueryOptionsfunksiyalariga yig'ing. enabled— so'rovni kutdiradi (isLoadingbilan ko'rsating),select— keshdan kerakli qismni oladi.- Bir nechta
useQuery— parallel; bog'liq so'rov — sharshara; soni noma'lum —useQueries.
Keyingi dars: TanStack Query III: mutatsiyalar va keshni yangilash — useMutation, invalidateQueries va setQueryData, optimistik yangilash va qaytarish; vazifalar dagi holat 14 ning commit'i.
Manbalar
- TanStack Query v5: Important Defaults — tanstack.com/query/v5/docs/framework/react/guides/important-defaults (2026-10, v5.104)
- TanStack Query v5: Caching Examples, Window Focus Refetching, Query Invalidation — …/guides/caching, …/guides/window-focus-refetching, …/guides/query-invalidation
- TanStack Query v5: Query Keys, Query Options, Dependent Queries, Parallel Queries — …/guides/query-keys, …/guides/query-options, …/guides/dependent-queries, …/guides/parallel-queries
- TkDodo: Effective React Query Keys — tkdodo.eu/blog/effective-react-query-keys
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!