12.3-bob: RTK Query
12-QISM — State Management va Data Fetching · 3-mavzu
1. Kirish va motivatsiya
12.2-bobda Redux Toolkit bilan client state'ni boshqardik va createAsyncThunk bilan server'dan ma'lumot olishni ko'rdik. Lekin u yerda muhim ogohlantirish bordi: server ma'lumoti uchun createAsyncThunk ko'p qo'lda kod (loading/error/kesh boshqaruvi) talab qiladi — har endpoint uchun thunk, slice, extraReducers (pending/fulfilled/rejected). Va eng muhimi — u keshlamaydi, fonda yangilamaydi, takror so'rovlarni birlashtirmaydi, eskirishni boshqarmaydi. Bularning hammasini qo'lda yozish — yuzlab qator zerikarli kod va ko'p xato. Aynan shu muammoni hal qilish uchun RTK Query yaratilgan.
RTK Query — Redux Toolkit'ning server-state yechimi (data fetching va caching kutubxonasi). U Redux ustiga qurilgan, lekin tubdan boshqacha maqsadga xizmat qiladi: client state (theme, cart) emas, server ma'lumotini (API javobi) professional boshqaradi. RTK Query bilan siz faqat API endpoint'larini ta'riflaysiz (qaysi URL, qanday so'rov), va u qolganini — kesh, fonda yangilash, qayta urinish, loading/error holatlari, takror so'rovlarni birlashtirish (deduplication), kerakli paytda qayta yuklash (invalidation) — barchasini avtomatik qiladi. Bundan tashqari, u har endpoint uchun avtomatik React hook generatsiya qiladi (useGetUsersQuery, useAddUserMutation) — siz faqat ishlatasiz.
Bu bob: server state muammosi (client vs server — 12.1: 2.10 davomi — chuqurroq), RTK Query nima (RTK ustiga — data fetching + caching), createApi va baseQuery (API ta'rifi), endpoints (query va mutation), avtomatik hooklar (useGetXQuery/useXMutation), keshlash va cache hayot sikli (cache key, staleness), tags va invalidation (kesh yangilash — eng muhim), loading/error holatlari (isLoading/isFetching/error), mutations va optimistic update, baseQuery sozlash (auth header, error handling), va RTK Query vs createAsyncThunk vs TanStack Query (qaysi qachon). RTK Query'ni to'liq — server-state falsafasi va amaliy holatlarda — ochamiz.
O'xshatish: RTK Query — bu aqlli oziq-ovqat ombori va yetkazib berish xizmati.
createAsyncThunk12.2-bob — bu har safar bozorga o'zingiz borish: ovqat kerak bo'lganda borasiz, sotib olasiz, uyga olib kelasiz — har gal noldan (sekin, takror). RTK Query — bu aqlli ombor: bir marta "menga sut kerak" desangiz, u sutni keltiradi va omborga solib qo'yadi (kesh) — keyingi safar so'rasangiz, ombordan darrov beradi (so'rov takror yuborilmaydi). Sut eskirsa (staleness), u fonda yangisini keltiradi (foydalanuvchi eskisini ko'rib turgan paytda). Va siz sutni o'zgartirsangiz (mutation — masalan "qaymoq qo'sh"), u tegishli mahsulotlarni ombordan bekor qiladi (invalidation) va yangisini keltiradi. Siz faqat "menga nima kerak"ni aytasiz (endpoint), qolganini — saqlash, yangilash, tozalash — aqlli ombor o'zi qiladi.
Nega muhim?
- Server state ≠ client state — server ma'lumoti o'z muammolariga ega (kesh, sync, eskirish), ular maxsus vositani talab qiladi.
- Kam kod, ko'p imkoniyat — RTK Query yuzlab qator qo'lda fetch/kesh kodini bir necha qatorga siqadi.
- Stack'da bor — bu stack'da RTK Query rejalashtirilgan (prompt stack'i).
- Redux ekotizimi — agar loyiha allaqachon Redux ishlatsa, RTK Query — tabiiy server-state yechimi.
2. Nazariya — chuqur tushuntirish
2.1. Server state muammosi (client vs server — chuqur)
IKKI XIL "STATE" (12.1: 2.10 — chuqurroq):
CLIENT STATE — brauzerda tug'iladi, ILOVA nazoratida:
- theme, modal ochiq/yopiq, forma, savat (lokal), til
- sinxron, darrov, faqat frontendga tegishli (Redux/Context/Zustand)
SERVER STATE — serverdan keladi, BOSHQA joyda saqlanadi (DB):
- foydalanuvchilar, mahsulotlar, buyurtmalar (API javobi)
- O'Z MUAMMOLARI bor (client state'da yo'q):
ASINXRON (so'rov vaqt oladi — loading/error)
KESH (har safar so'ramaslik — saqlash)
ESKIRISH (server'da o'zgardi — frontend eski ko'rsatadi — sync)
TAKROR (ikki komponent bir ma'lumotni so'rasa — bir so'rov — dedupe)
FONDA YANGILASH (foydalanuvchi eski ko'pib turgan paytda yangisini olish)
Server state — Redux/Context'da qo'lda boshqarish = yuzlab qator + ko'p xato
Server state — MAXSUS vosita (RTK Query / TanStack Query) — barcha muammoni AVTOMATIKServer state muammosi — RTK Query'ning (va umuman data-fetching kutubxonalarining) mavjudlik sababi (12.1: 2.10 chuqurroq). Ikki tubdan boshqacha "state" bor. Client state — brauzerda tug'iladi va to'liq sizning nazoratingizda (theme, modal holati, forma, lokal savat, til) — sinxron, darrov, faqat sizga tegishli (Redux/Context/Zustand bilan boshqariladi). Server state — serverdan keladi va aslida boshqa joyda (ma'lumotlar bazasida) saqlanadi (foydalanuvchilar, mahsulotlar, buyurtmalar — API javobi). Server state'ning o'z muammolari bor (client state'da yo'q): (1) asinxron (so'rov vaqt oladi — loading/error holatlari); (2) kesh (har safar so'ramaslik — bir marta olib saqlash); (3) eskirish (server'da ma'lumot o'zgardi — frontend eski ko'rsatadi — sinxronlash kerak); (4) takror (ikki komponent bir ma'lumotni so'rasa — bitta so'rov yetadi — deduplication); (5) fonda yangilash (foydalanuvchi eski ma'lumotni ko'pib turgan paytda yangisini olib yangilash). Ikki nuqta: (1) server state'ni Redux/Context'da qo'lda boshqarish — yuzlab qator kod va ko'p xato (chunki bu muammolarni qo'lda hal qilish qiyin); (2) server state — maxsus vosita (RTK Query / TanStack Query) talab qiladi, ular barcha bu muammoni avtomatik hal qiladi. Bu farq — zamonaviy frontend arxitekturasining markaziy tushunchasi.
2.2. RTK Query nima va arxitekturasi
RTK QUERY — Redux Toolkit'ning server-state (data fetching + caching) yechimi:
@reduxjs/toolkit ICHIDA keladi (alohida o'rnatish kerak emas)
ARXITEKTURA:
┌────────────────────────────────────────────────────────────┐
│ createApi — API "ta'rifi" (endpoint'lar + baseQuery) │
│ │
│ endpoints — query (o'qish) + mutation (o'zgartirish) │
│ │
│ AVTOMATIK HOOKLAR — useGetUsersQuery, useAddUserMutation │
│ │
│ Komponent — hook'ni ishlatadi (data, isLoading, error) │
│ │
│ + RTK Query KESHNI Redux store'da saqlaydi (avtomatik) │
└────────────────────────────────────────────────────────────┘
RTK QUERY AVTOMATIK QILADIGAN ISHLAR:
Keshlash (cache key bo'yicha — 2.6) Loading/error holatlari
Deduplication (takror so'rov birlashadi) Fonda yangilash (refetch)
Invalidation (tags — kerakli paytda qayta — 2.7) Avtomatik hooklar
RTK Query — RTK ichida (Redux loyihada tabiiy); kesh Redux store'da
Faqat endpoint ta'riflanadi hooklar va kesh AVTOMATIKRTK Query nima va arxitekturasi — Redux Toolkit'ning server-state yechimi (
@reduxjs/toolkitichida keladi — alohida o'rnatish kerak emas). Arxitektura: (1)createApi— API "ta'rifi" (barcha endpoint'lar +baseQuery— asosiy so'rov mexanizmi); (2) endpoints — query (o'qish — GET) va mutation (o'zgartirish — POST/PUT/DELETE); (3) avtomatik hooklar — RTK Query har endpoint uchun React hook generatsiya qiladi (useGetUsersQuery,useAddUserMutation); (4) komponent — shu hook'ni ishlatadi (data,isLoading,error). Va eng muhimi — RTK Query keshni Redux store'da avtomatik saqlaydi. RTK Query avtomatik qiladigan ishlar: keshlash (cache key bo'yicha — 2.6), loading/error holatlari, deduplication (takror so'rov birlashadi), fonda yangilash, invalidation (tags — kerakli paytda qayta so'rov — 2.7), avtomatik hooklar. Ikki nuqta: (1) RTK Query — RTK ichida (agar loyiha allaqachon Redux ishlatsa — tabiiy server-state yechimi; kesh Redux store'da saqlanadi); (2) siz faqat endpoint'larni ta'riflaysiz (URL, metod) — hooklar va kesh boshqaruvi avtomatik. Bu — server state'ni minimal kod bilan professional boshqarish.
2.3. createApi va baseQuery
createApi — barcha API endpoint'larini BIR joyda ta'riflash:
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const api = createApi({
reducerPath: "api", // store'dagi joy nomi
baseQuery: fetchBaseQuery({ // asosiy so'rov mexanizmi (fetch o'rovi)
baseUrl: "/api", // barcha so'rov shu URL'dan
}),
tagTypes: ["User", "Product"], // kesh teglari (invalidation — 2.7)
endpoints: (builder) => ({ // endpoint'lar 2.4-bob
getUsers: builder.query({ // o'qish (query)
query: () => "/users", // GET /api/users
}),
addUser: builder.mutation({ // o'zgartirish (mutation)
query: (newUser) => ({ url: "/users", method: "POST", body: newUser }),
}),
}),
});
// Avtomatik hooklar (endpoint nomidan — 2.5):
export const { useGetUsersQuery, useAddUserMutation } = api;
createApi — bitta API "ta'rifi" (baseQuery + endpoints + tagTypes)
baseQuery — asosiy so'rov (fetchBaseQuery — fetch o'rovi; yoki axios — 2.10)
createApivabaseQuery— RTK Query'ning markaziy ta'rifi.createApi— barcha API endpoint'larni bitta joyda ta'riflaydi: (1)reducerPath(store'dagi joy nomi —"api"); (2)baseQuery— asosiy so'rov mexanizmi (fetchBaseQuery({ baseUrl: "/api" })—fetchning o'rovi, barcha so'rov shu URL'dan); (3)tagTypes— kesh teglari (invalidation uchun — 2.7); (4)endpoints— builder funksiyasi orqali query va mutation'lar 2.4-bob. Misol:getUsers: builder.query({ query: () => "/users" })(GET /api/users),addUser: builder.mutation({ query: (user) => ({ url: "/users", method: "POST", body: user }) }). RTK Query har endpoint uchun avtomatik hook generatsiya qiladi (useGetUsersQuery,useAddUserMutation— 2.5). Ikki nuqta: (1)createApi— bitta API "ta'rifi" (baseQuery+ endpoints + tagTypes) — odatda butun ilova uchun bittaapi(yoki feature bo'yicha bo'lish mumkin); (2)baseQuery— asosiy so'rov:fetchBaseQuery(fetch o'rovi — eng keng) yoki custom (axios bilan, yoki auth header/refresh logikasi bilan — 2.10). Bu — RTK Query setup'ining yuragi.
2.4. Endpoints — query va mutation
IKKI XIL ENDPOINT:
QUERY — ma'lumot O'QISH (GET — kesh qilinadi):
getProducts: builder.query({
query: () => "/products", // GET /api/products
}),
getProductById: builder.query({
query: (id) => `/products/${id}`, // argument bilan (GET /api/products/5)
}),
MUTATION — ma'lumot O'ZGARTIRISH (POST/PUT/DELETE — keshni bekor qiladi):
addProduct: builder.mutation({
query: (newProduct) => ({ // obyekt qaytaradi (URL + metod + body)
url: "/products", method: "POST", body: newProduct,
}),
}),
deleteProduct: builder.mutation({
query: (id) => ({ url: `/products/${id}`, method: "DELETE" }),
}),
┌────────────────────────────────────────────────────────────┐
│ query: ma'lumot o'qish (GET, keshlanadi — 2.6) │
│ mutation: ma'lumot o'zgartirish (POST/PUT/DELETE, kesh bekor) │
└────────────────────────────────────────────────────────────┘
query — o'qish (keshlanadi, avtomatik); mutation — o'zgartirish (qo'lda trigger)
query string yoki obyekt qaytaradi (obyekt: url/method/body — murakkab so'rov)Endpoints — query va mutation — RTK Query'ning ikki turli endpoint turi. Query — ma'lumot o'qish (GET):
getProducts: builder.query({ query: () => "/products" })(GET /api/products), yoki argument bilangetProductById: builder.query({ query: (id) => \/products/${id}` }). Query'lar **avtomatik keshlanadi** (cache key — argument bo'yicha — 2.6) va komponent mount bo'lganda avtomatik ishlaydi. **Mutation** — ma'lumot **o'zgartirish** (POST/PUT/DELETE):addProduct: builder.mutation({ query: (newProduct) => ({ url: "/products", method: "POST", body: newProduct }) })—queryobyekt qaytaradi (url,method,body). Mutation'lar **qo'lda** ishga tushiriladi (foydalanuvchi tugmani bosganda — trigger funksiya) va odatda keshni **bekor qiladi** (invalidation — 2.7 — masalan yangi mahsulot qo'shilsa, mahsulotlar ro'yxatining keshi eskiradi qayta so'rov). Ikki nuqta: (1) **query** — o'qish (avtomatik, keshlanadi); **mutation** — o'zgartirish (qo'lda trigger, keshni bekor qiladi); (2)queryfunksiyasi oddiy string (GET URL) yoki obyekt (url/method/body` — murakkab so'rov uchun) qaytaradi. Bu ikki tur — server bilan to'liq ishlashning (o'qish + o'zgartirish) asosi.
2.5. Avtomatik hooklar
RTK QUERY har endpoint uchun AVTOMATIK React hook generatsiya qiladi:
endpoint nomi hook nomi:
getUsers (query) useGetUsersQuery
getUserById (query) useGetUserByIdQuery
addUser (mutation) useAddUserMutation
deleteUser (mutation) useDeleteUserMutation
QUERY HOOK (o'qish — komponentda avtomatik ishlaydi):
const { data, isLoading, isFetching, error, refetch } = useGetUsersQuery();
const { data: user } = useGetUserByIdQuery(5); // argument bilan
MUTATION HOOK (o'zgartirish — qo'lda trigger):
const [addUser, { isLoading, isSuccess }] = useAddUserMutation();
await addUser({ name: "Ali" }); // trigger (so'rov yuboriladi)
┌────────────────────────────────────────────────────────────┐
│ Query hook: useXQuery() {data, isLoading, error} (avtomatik)│
│ Mutation hook: [trigger, {isLoading}] = useXMutation() │
└────────────────────────────────────────────────────────────┘
Hooklar AVTOMATIK (endpoint nomidan — yozish kerak emas)
Query — avtomatik ishlaydi; mutation — trigger funksiya bilan (qo'lda)Avtomatik hooklar — RTK Query'ning eng qulay xususiyati. RTK Query har endpoint uchun avtomatik React hook generatsiya qiladi (endpoint nomidan):
getUsersuseGetUsersQuery,addUseruseAddUserMutation. Query hook — komponentda avtomatik ishlaydi:const { data, isLoading, isFetching, error, refetch } = useGetUsersQuery()— komponent mount bo'lganda so'rov yuboriladi (yoki kesh bo'lsa keshdan), va holatlar (data,isLoading,error) avtomatik beriladi; argument bilanuseGetUserByIdQuery(5). Mutation hook — qo'lda trigger:const [addUser, { isLoading, isSuccess }] = useAddUserMutation()—addUser({ name: "Ali" })chaqirganda so'rov yuboriladi (massiv qaytaradi: birinchi — trigger funksiya, ikkinchi — holat obyekti). Ikki nuqta: (1) hooklar avtomatik (endpoint nomidan — qo'lda yozish kerak emas — RTK Query generatsiya qiladi); (2) query — avtomatik ishlaydi (komponent mount'da); mutation — trigger funksiya bilan (foydalanuvchi harakati natijasida). Bu — server bilan ishlashni minimal kodga (deyarli faqat hook chaqirish) qisqartiradi.
2.6. Keshlash va cache hayot sikli
KESHLASH — RTK Query so'rov natijasini SAQLAYDI (cache key bo'yicha):
CACHE KEY — endpoint + argument (har xil argument — har xil kesh):
useGetUserByIdQuery(5) cache key: "getUserById(5)"
useGetUserByIdQuery(7) cache key: "getUserById(7)" (alohida kesh)
KESH HAYOT SIKLI:
1. 1-komponent useGetUsersQuery() so'rov yuboriladi natija KESHGA
2. 2-komponent useGetUsersQuery() KESHDAN (so'rov takror YUBORILMAYDI — dedupe)
3. Komponent unmount kesh saqlanadi (default 60s — keepUnusedDataFor)
4. 60s'dan keyin hech kim ishlatmasa kesh O'CHIRILADI
STALENESS (eskirish) — kesh "eski" bo'lsa fonda yangilash:
- refetchOnMountOrArgChange — mount'da yangilash
- refetchOnFocus — oyna fokusga qaytganda
- refetchOnReconnect — internet qayta ulanganda
Kesh key — endpoint + argument (har argument alohida kesh)
Deduplication — ko'p komponent bir query BITTA so'rov (kesh ulashiladi)Keshlash va cache hayot sikli — RTK Query'ning markaziy imkoniyati. RTK Query so'rov natijasini keshga saqlaydi, va kesh cache key bo'yicha tashkil etiladi — bu endpoint + argument (har xil argument — har xil kesh:
useGetUserByIdQuery(5)va(7)— alohida kesh). Kesh hayot sikli: (1) birinchi komponentuseGetUsersQuery()chaqirsa — so'rov yuboriladi va natija keshga saqlanadi; (2) ikkinchi komponent xuddi shu query'ni chaqirsa — natija keshdan olinadi (so'rov takror yuborilmaydi — bu deduplication); (3) komponent unmount bo'lganda kesh saqlanadi (default 60 soniya —keepUnusedDataFor); (4) 60 soniya davomida hech kim ishlatmasa — kesh o'chiriladi. Staleness (eskirish) — kesh "eski" deb hisoblansa fonda yangilash:refetchOnMountOrArgChange(mount yoki argument o'zgarganda),refetchOnFocus(oyna fokusga qaytganda — masalan boshqa tab'dan qaytib kelganda),refetchOnReconnect(internet qayta ulanganda). Ikki nuqta: (1) cache key — endpoint + argument (har argument alohida keshlanadi); (2) deduplication — ko'p komponent bir xil query'ni ishlatsa, bitta so'rov yuboriladi va natija ulashiladi (samaralilik). Bu — server ma'lumotini samarali, kam so'rov bilan boshqarish.
2.7. Tags va invalidation — kesh yangilash
MUAMMO: mahsulot QO'SHILDI (mutation) mahsulotlar RO'YXATI (query) eskirdi qanday yangilash?
TAGS va INVALIDATION — RTK Query'ning kesh-sinxronlash mexanizmi:
1. tagTypes — kesh teglari ta'riflanadi (createApi'da):
tagTypes: ["Product"],
2. QUERY — "men shu tegni TA'MINLAYMAN" (provides):
getProducts: builder.query({
query: () => "/products",
providesTags: ["Product"], // bu query — "Product" teg
}),
3. MUTATION — "men shu tegni BEKOR QILAMAN" (invalidates):
addProduct: builder.mutation({
query: (p) => ({ url: "/products", method: "POST", body: p }),
invalidatesTags: ["Product"], // "Product" query'lar QAYTA so'raladi
}),
addProduct chaqirilsa "Product" teg bekor getProducts AVTOMATIK qayta yuklanadi
providesTags (query — ta'minlaydi) + invalidatesTags (mutation — bekor qiladi)
Mutation'dan keyin tegishli query AVTOMATIK qayta yuklanadi (qo'lda refetch kerak emas)Tags va invalidation — RTK Query'ning eng muhim va eng kuchli mexanizmi (kesh-sinxronlash). Muammo: mahsulot qo'shilsa (mutation), mahsulotlar ro'yxati (query) eskiradi — frontend eski ro'yxatni ko'rsatadi, qanday yangilash? Qo'lda refetch — zerikarli va xatoga moyil. Yechim — tags (teglar): (1)
createApi'datagTypes: ["Product"](mavjud teglar); (2) query o'z keshini tegga bog'laydi —providesTags: ["Product"]("bu query — Product ma'lumotini ta'minlaydi"); (3) mutation tegni bekor qiladi —invalidatesTags: ["Product"]("bu mutation Product ma'lumotini o'zgartirdi"). Natijada:addProductchaqirilganda "Product" teg bekor bo'ladi "Product" tegli barcha query (getProducts) avtomatik qayta yuklanadi (eng yangi ma'lumot bilan). Bu — qo'lda refetch'siz, deklarativ kesh-sinxronlash. Ikki nuqta: (1)providesTags(query — tegni ta'minlaydi) +invalidatesTags(mutation — tegni bekor qiladi) — birga ishlaydi; (2) mutation'dan keyin tegishli query avtomatik qayta yuklanadi (qo'ldarefetchchaqirish kerak emas — RTK Query biladi qaysi query'ni yangilash). ID bo'yicha aniqroq invalidation ham mumkin (providesTags: (result) => result.map(p => ({ type: "Product", id: p.id }))). Bu — RTK Query'ni server bilan to'liq sinxron tutishning asosi.
2.8. Loading va error holatlari
RTK QUERY HOLATLARI (query hook):
isLoading — BIRINCHI yuklanish (hali ma'lumot yo'q — spinner)
isFetching — har QANDAY yuklanish (birinchi + fonda yangilash — indikator)
isSuccess — muvaffaqiyatli (data bor)
isError — xato (error bor)
error — xato obyekti
data — ma'lumot (kesh yoki yangi)
refetch — qo'lda qayta yuklash
isLoading vs isFetching (MUHIM farq):
isLoading — faqat BIRINCHI marta (kesh yo'q — to'liq spinner)
isFetching — fonda yangilash ham (kesh bor, lekin yangilanmoqda — kichik indikator)
function Products() {
const { data, isLoading, isFetching, error } = useGetProductsQuery();
if (isLoading) return <Skeleton />; // birinchi — skeleton
if (error) return <Error />;
return (
<div>
{isFetching && <small>Yangilanmoqda...</small>} {/* fonda — kichik */}
<List items={data} />
</div>
);
}
isLoading — birinchi yuklash (spinner); isFetching — har yuklash (fonda — indikator)
Kesh bor darrov data ko'rsatiladi (isFetching=true — fonda yangilanadi — UX)Loading va error holatlari — RTK Query query hook'ining holatlari (loading boshqaruvi avtomatik). Asosiy holatlar:
isLoading(birinchi yuklanish — hali ma'lumot yo'q — to'liq spinner/skeleton),isFetching(har qanday yuklanish — birinchi + fonda yangilash — kichik indikator),isSuccess,isError,error(xato obyekti),data(ma'lumot — kesh yoki yangi),refetch(qo'lda qayta yuklash).isLoadingvsisFetching(muhim farq):isLoading— faqat birinchi marta (kesh yo'q — to'liq yuklanish — spinner ko'rsatiladi);isFetching— fonda yangilash ham (kesh bor, ma'lumot darrov ko'rinadi, lekin orqa fonda yangilanmoqda — kichik "yangilanmoqda" indikatori). Bu farq UX'ni yaxshilaydi: kesh bor bo'lsa foydalanuvchi darrov ma'lumotni ko'radi (isLoadingfalse), vaisFetchingorqali fonda yangilanayotganini kichik belgi bilan ko'rsatasiz (to'liq spinner emas). Ikki nuqta: (1)isLoading— birinchi yuklanish (spinner/skeleton);isFetching— har yuklanish (fonda — kichik indikator); (2) kesh bor bo'lganda ma'lumot darrov ko'rsatiladi va fonda yangilanadi (eng yaxshi UX — eski ma'lumot + yangilanish — "stale-while-revalidate"). Bu holatlar 11.15: 2.8 dagi 4 holatni (loading/error/empty/data) avtomatik beradi.
2.9. Mutations va optimistic update
MUTATION — server ma'lumotini o'zgartirish (POST/PUT/DELETE):
const [addProduct, { isLoading }] = useAddProductMutation();
await addProduct(newProduct).unwrap(); // .unwrap() — natija/xato Promise sifatida
OPTIMISTIC UPDATE — server javobini KUTMASDAN UI'ni darrov yangilash (tez UX):
foydalanuvchi "yoqdi" bosdi darrov (server javobini kutmasdan)
server muvaffaqiyatli o'zgardi (qoladi); xato ORQAGA qaytarish (rollback)
addProduct: builder.mutation({
query: (p) => ({ url: "/products", method: "POST", body: p }),
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
// 1. Darrov keshni yangila (optimistic):
const patch = dispatch(api.util.updateQueryData("getProducts", undefined, (draft) => {
draft.push(arg); // UI darrov yangilanadi
}));
try {
await queryFulfilled; // server javobini kut
} catch {
patch.undo(); // xato ORQAGA qaytar (rollback)
}
},
}),
Optimistic update — UI darrov (server kutmasdan) tez his; xato bo'lsa rollback
.unwrap() — mutation natijasini Promise sifatida (try/catch bilan boshqarish)Mutations va optimistic update — server'ni o'zgartirish va tez UX. Mutation — server ma'lumotini o'zgartirish:
const [addProduct, { isLoading }] = useAddProductMutation(), keyinawait addProduct(newProduct).unwrap()—.unwrap()natijani yoki xatoni Promise sifatida beradi (try/catch bilan boshqarish uchun). Optimistic update — server javobini kutmasdan UI'ni darrov yangilash (eng tez UX): masalan foydalanuvchi "yoqdi" tugmasini bosganda, darrov (server javobini kutmasdan), keyin server muvaffaqiyatli bo'lsa o'zgarish qoladi, xato bo'lsa orqaga qaytarish (rollback). RTK Query'da buonQueryStartedbilan amalga oshiriladi: (1) darrov keshni yangilash (dispatch(api.util.updateQueryData(...))— UI darrov o'zgaradi); (2)await queryFulfilled(server javobini kut); (3)catchichidapatch.undo()(xato bo'lsa rollback). Ikki nuqta: (1) optimistic update — UI darrov yangilanadi (server kutmasdan — tez his), xato bo'lsa rollback (orqaga); (2).unwrap()— mutation natijasini Promise sifatida (muvaffaqiyat/xatonitry/catchbilan boshqarish — masalan toast ko'rsatish). Optimistic update — "yoqdi", "saqlash", "qo'shish" kabi tez-tez bajariladigan amallar uchun ideal (foydalanuvchi kutmaydi). Bu — professional, silliq UX'ning belgisi.
2.10. baseQuery sozlash — auth va error handling
baseQuery'ni sozlash (auth header, refresh token, xato boshqaruvi):
// Auth header (har so'rovga token — 11.15: 2.7 interceptor kabi):
baseQuery: fetchBaseQuery({
baseUrl: "/api",
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token;
if (token) headers.set("Authorization", `Bearer ${token}`); // har so'rovga
return headers;
},
}),
// Refresh token bilan (401 token yangilash qayta so'rov) — custom baseQuery:
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions);
if (result.error?.status === 401) { // token tugadi
const refresh = await baseQuery("/auth/refresh", api, extraOptions);
if (refresh.data) {
api.dispatch(setToken(refresh.data.token)); // yangi token
result = await baseQuery(args, api, extraOptions); // so'rovni qayta
} else api.dispatch(logout());
}
return result;
};
prepareHeaders — har so'rovga token (axios interceptor'ning RTK Query versiyasi — 11.15)
Custom baseQuery — 401/refresh logikasi (token yangilash — avtomatik)
baseQuerysozlash — auth va error handling (real ilovada zarur). Auth header — har so'rovga token qo'shish (11.15: 2.7 axios interceptor'ning RTK Query versiyasi):fetchBaseQuery({ baseUrl, prepareHeaders: (headers, { getState }) => { const token = getState().auth.token; if (token) headers.set("Authorization", \Bearer ${token}`); return headers } })—prepareHeadershar so'rovdan oldin ishlaydi, store'dan token olib header'ga qo'shadi. **Refresh token bilan** (401 token yangilash so'rovni qayta yuborish) — bu uchun **custom baseQuery** yoziladi: asosiy baseQuery'ni o'rab, agar javob401(token tugadi) bo'lsa — refresh token bilan yangi token olib, so'rovni **qayta** yuboradi (yoki refresh ham ishlamasa — logout). Ikki nuqta: (1) **prepareHeaders`** — har so'rovga token (markazlashgan auth — har endpoint'da qo'lda qo'shish kerak emas); (2) custom baseQuery — 401/refresh logikasi (token muddati tugaganda avtomatik yangilash — foydalanuvchi qayta login qilmacdan — 11.15: 2.7). Bu — production ilovada RTK Query'ni auth bilan ishlatishning standart naqshi (interceptor mantig'i RTK Query'ga ko'chirilgan).
2.11. RTK Query vs createAsyncThunk vs TanStack Query
QAYSI QACHON (server-state vositalari):
┌─────────────────────┬──────────────────────────────────────┐
│ createAsyncThunk │ oddiy async / client-side async amal │
│ 12.2-bob │ (kesh/sync kerak emas — bir martalik) │
│ RTK Query │ Redux loyihada server state │
│ (bu bob) │ (kesh/invalidation — RTK ekotizimida) │
│ TanStack Query │ universal server state (Redux'siz ham) │
│ 12.4-bob │ (eng keng, eng moslashuvchan — 12.4) │
└─────────────────────┴──────────────────────────────────────┘
RTK QUERY vs TANSTACK QUERY (eng keng savol):
- RTK Query — Redux ICHIDA (agar loyiha Redux ishlatsa — tabiiy); kesh Redux store'da
- TanStack Query — MUSTAQIL (Redux kerak emas); ko'proq moslashuvchan, keng community
Redux loyiha RTK Query; Redux'siz / yangi loyiha TanStack Query (ko'pincha afzal)
Server state createAsyncThunk EMAS (kesh/sync yo'q) RTK Query / TanStack Query
Redux loyiha RTK Query; aks holda TanStack Query (12.4 — eng keng tavsiya)RTK Query vs createAsyncThunk vs TanStack Query — server-state vositasini tanlash qarori.
createAsyncThunk12.2-bob — oddiy async yoki client-side async amal uchun (kesh/sync kerak bo'lmagan bir martalik so'rov). RTK Query (bu bob) — Redux loyihada server state uchun (kesh/invalidation — RTK ekotizimida, kesh Redux store'da). TanStack Query 12.4-bob — universal server state uchun (Redux kerak emas — mustaqil, eng keng va moslashuvchan). RTK Query vs TanStack Query (eng keng savol): RTK Query — Redux ichida (agar loyiha allaqachon Redux ishlatsa — tabiiy, qo'shimcha kutubxona kerak emas; kesh Redux store'da saqlanadi); TanStack Query — mustaqil (Redux'dan bog'liq emas, ko'proq moslashuvchan, kattaroq community, ko'p funksiya). To'g'ri tanlov: agar loyiha allaqachon Redux ishlatsa RTK Query (tabiiy); Redux'siz yoki yangi loyiha TanStack Query (ko'pincha afzal — 12.4). Ikki nuqta: (1) server state uchuncreateAsyncThunkemas (kesh/sync yo'q — qo'lda og'riq) RTK Query yoki TanStack Query; (2) Redux loyiha RTK Query; aks holda TanStack Query (12.4 — bugun eng keng tavsiya etiladigan server-state vositasi). Ikkalasi ham server state muammolarini 2.1-bob hal qiladi — farq ekotizim va moslashuvchanlikda. Bu qaror — frontend arxitektura tanlovining muhim qismi.
2.12. LIST tag pattern — aniq invalidation
MUAMMO: providesTags: ["Product"] — juda "keng":
bitta mahsulot o'zgarsa ham BUTUN ro'yxat qayta so'raladi (ortiqcha)
yangi mahsulot QO'SHILGANDA esa ro'yxat yangilanishi KERAK (yangi element)
LIST TAG PATTERN — ikki xil teg: har element ID + maxsus LIST tegi:
getProducts: builder.query({
query: () => "/products",
providesTags: (result) =>
result
? [
...result.map((p) => ({ type: "Product", id: p.id })), // har element
{ type: "Product", id: "LIST" }, // butun ro'yxat tegi
]
: [{ type: "Product", id: "LIST" }],
}),
addProduct: builder.mutation({ // YANGI qo'shildi LIST eskirdi
invalidatesTags: [{ type: "Product", id: "LIST" }], // faqat ro'yxat qayta
}),
updateProduct: builder.mutation({ // BITTA o'zgardi faqat o'sha
invalidatesTags: (r, e, { id }) => [{ type: "Product", id }], // faqat shu element
}),
id:"LIST" — "ro'yxatning o'zi" tegi (qo'shish/o'chirishda invalidate)
id:p.id — har element tegi (o'zgarishda faqat shu element invalidate)LIST tag pattern — invalidation'ni aniq va tejamkor qilishning RTK Query'dagi rasmiy nashti. Muammo: oddiy
providesTags: ["Product"]juda keng — bitta mahsulot o'zgarsa ham butun ro'yxat qayta so'raladi (ortiqcha tarmoq so'rovi); lekin yangi element qo'shilganda ro'yxat yangilanishi shart. Bu ikki holatni ajratish uchun LIST tag pattern ishlatiladi: query o'z keshini ikki turdagi tegga bog'laydi — (1) har element uchun{ type: "Product", id: p.id }(aniq element tegi) va (2) butun ro'yxat uchun maxsus{ type: "Product", id: "LIST" }(ro'yxatning o'zi tegi). Endi mutation'lar aniq invalidate qiladi: qo'shish (addProduct) faqatid: "LIST"(yangi element ro'yxatga chiqishi kerak, lekin mavjud elementlar keshi buzilmaydi); o'zgartirish (updateProduct) faqat{ type: "Product", id }(o'sha bitta element qayta so'raladi, ro'yxat butunligicha emas); o'chirishid: "LIST"(ro'yxatdan tushishi kerak). Natijada — minimal so'rov bilan maksimal to'g'rilik. Ikki nuqta: (1)id: "LIST"— "ro'yxatning o'zi" uchun sun'iy teg (qo'shish/o'chirishda invalidate qilinadi); (2)id: p.id— har element uchun alohida teg (bitta element o'zgarganda faqat o'sha element yangilanadi). Bu pattern — RTK Query bilan katta ro'yxatlarni samarali boshqarishning standarti (Misol 15).
2.13. Pagination va infinite scroll
PAGINATION — sahifalab yuklash (page argument — har sahifa alohida kesh):
getProducts: builder.query({
query: (page = 1) => `/products?page=${page}&limit=20`, // page argument
}),
// Komponent: useGetProductsQuery(page) — har page alohida cache key 2.6-bob
INFINITE (cheksiz — "ko'proq yuklash"): eski + yangi sahifani BIRLASHTIRISH.
Klassik yo'l — serializeQueryArgs + merge + forceRefetch:
getFeed: builder.query({
query: (page) => `/feed?page=${page}`,
serializeQueryArgs: ({ endpointName }) => endpointName, // hamma page — bitta kesh
merge: (currentCache, newItems) => {
currentCache.push(...newItems); // eski + yangi (Immer)
},
forceRefetch: ({ currentArg, previousArg }) => currentArg !== previousArg,
}),
YANGI (RTK Query 2.x): infiniteQuery endpoint (rasmiy infinite API):
getFeed: builder.infiniteQuery({
query: ({ pageParam }) => `/feed?page=${pageParam}`,
infiniteQueryOptions: {
initialPageParam: 1,
getNextPageParam: (last, all, lastArg) => lastArg + 1, // keyingi sahifa argi
},
}),
// hook: const { data, fetchNextPage, hasNextPage } = useGetFeedInfiniteQuery();
Oddiy pagination — page argument (har sahifa alohida kesh, "1/2/3" tugmalar)
Infinite — merge (eski+yangi bitta ro'yxatda) yoki infiniteQuery (RTK 2.x rasmiy)Pagination va infinite scroll — katta ro'yxatlarni bo'lib yuklash. Oddiy pagination (raqamli sahifalar "1 2 3") — eng sodda:
query: (page) => \/products?page=${page}`va komponentdauseGetProductsQuery(page). Bunda **har sahifa alohida cache key** 2.6-bob bo'ladi, ya'ni 2-sahifadan 1-sahifaga qaytilsa — keshdan darrov ko'rinadi (qayta so'rov yo'q). **Infinite scroll** ("Ko'proq yuklash" / pastga aylantirganda avtomatik) esa boshqacha: eski sahifalarni **saqlab**, yangi sahifani ularga **qo'shish** kerak (bitta uzun ro'yxat). Buni ikki yo'l bilan qilish mumkin: (1) klassik yo'l —serializeQueryArgs(barcha sahifani bitta cache key'ga),merge(yangi elementlarni mavjud keshga qo'shish — Immerdraft),forceRefetch(argument o'zgarganda yangi so'rovga ruxsat); (2) RTK Query 2.x'dagi rasmiy **builder.infiniteQuery** —initialPageParam(boshlang'ich sahifa),getNextPageParam(keyingi sahifa argumentini hisoblash), va hookfetchNextPage/hasNextPageberadi (TanStack Query'ninguseInfiniteQueryiga o'xshash). Ikki nuqta: (1) oddiy pagination — page argument (har sahifa alohida keshlanadi); (2) infinite —mergeyokiinfiniteQuery` (eski + yangi elementlar bitta ro'yxatda birlashadi). Bu — cheksiz lentalar (feed, natijalar) uchun asosiy naqsh (Misol 16).
2.14. Conditional fetch (skip) va prefetch
SKIP — so'rovni SHARTGA bog'lash (masalan token yo'qsa so'ramaslik):
const { data } = useGetProfileQuery(userId, {
skip: !userId, // userId yo'q bo'lsa — so'rov YUBORILMAYDI
});
// yoki skipToken (TypeScript'da toza):
useGetProfileQuery(userId ?? skipToken);
PREFETCH — ma'lumotni OLDINDAN yuklash (foydalanuvchi bosishidan oldin — tezlik):
const prefetch = usePrefetch("getProductById");
<li onMouseEnter={() => prefetch(product.id)}> // hover'da oldindan yukla
{product.name} // bosganda darrov (kesh tayyor)
</li>
skip — shartli so'rov (auth/argument tayyor bo'lmaguncha yubormaslik)
prefetch — hover/oldindan yuklash bosganda darrov ko'rinadi (silliq navigatsiya)Conditional fetch (skip) va prefetch — so'rovni boshqarishning ikki muhim vositasi. Skip — query'ni shartga bog'lash: query hook mount bo'lganda avtomatik ishlaydi, lekin ba'zan argument hali tayyor emas (masalan
userIdyuklanmagan) yoki foydalanuvchi login qilmagan — bunda so'rov yubormaslik kerak.useGetProfileQuery(userId, { skip: !userId })—skiptruebo'lsa so'rov yuborilmaydi (isLoadingfalseqoladi). TypeScript'da toza yo'l —skipToken:useGetProfileQuery(userId ?? skipToken)(budataturini ham to'g'ri boshqaradi). Prefetch — ma'lumotni oldindan yuklash (foydalanuvchi bosishidan oldin):const prefetch = usePrefetch("getProductById"), keyinonMouseEnter={() => prefetch(id)}— foydalanuvchi elementga sichqoncha olib borganda kesh oldindan to'ldiriladi, bosganda ma'lumot darrov ko'rinadi (silliq navigatsiya, "0ms" his). Ikki nuqta: (1) skip — shartli so'rov (argument/auth tayyor bo'lmaguncha yubormaslik — ortiqcha yoki xato so'rovning oldini oladi); (2) prefetch — hover yoki route o'zgarishidan oldin yuklash (bosganda darrov — professional UX). Bu ikkovi — so'rov vaqtini nazorat qilishning nozik vositalari.
2.15. Retry, streaming updates (WebSocket) va code generation
RETRY — xatoda avtomatik qayta urinish (tarmoq nosozligi):
import { retry } from "@reduxjs/toolkit/query/react";
baseQuery: retry(fetchBaseQuery({ baseUrl: "/api" }), { maxRetries: 3 }),
// yoki endpoint'da: extraOptions: { maxRetries: 5 }
// retry.fail(error) — ma'lum xatolarda urinishni to'xtatish
STREAMING UPDATES — WebSocket bilan keshni JONLI yangilash (real-time chat):
getMessages: builder.query({
query: () => "/messages",
async onCacheEntryAdded(arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved }) {
const ws = new WebSocket("wss://api/ws");
await cacheDataLoaded; // dastlabki ma'lumot yuklanmaguncha kut
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
updateCachedData((draft) => { draft.push(msg); }); // keshga JONLI qo'sh (Immer)
};
await cacheEntryRemoved; // komponent ketganda
ws.close(); // socket'ni yop (tozalash)
},
}),
CODE GENERATION — OpenAPI'dan endpoint'larni AVTOMATIK yaratish (backend sxemasidan):
npx @rtk-query/codegen-openapi openapi-config.ts
// OpenAPI/Swagger sxema typed endpoint'lar + hooklar (qo'lda yozish shart emas)
retry — tarmoq xatosida avtomatik qayta urinish (maxRetries)
onCacheEntryAdded — WebSocket/SSE bilan keshni jonli yangilash (chat/notif)
codegen-openapi — backend sxemasidan endpoint+hook avtomatik (katta API'da vaqt tejaydi)Retry, streaming updates va code generation — RTK Query'ning ilg'or (advanced) imkoniyatlari. Retry — so'rov tarmoq xatosi bilan tugasa avtomatik qayta urinish:
baseQuery: retry(fetchBaseQuery(...), { maxRetries: 3 })(yoki endpoint darajasidaextraOptions: { maxRetries }). Vaqtinchalik nosozliklarni (bir zumga uzilgan tarmoq) foydalanuvchiga bildirmasdan yengish uchun ideal;retry.fail(error)bilan ma'lum xatolarda (masalan 404) urinishni to'xtatish mumkin. Streaming updates — keshni real-time (WebSocket/SSE) orqali jonli yangilash:onCacheEntryAddedlifecycle'i ichida WebSocket ochiladi,cacheDataLoadedkutiladi (dastlabki HTTP ma'lumoti yuklanguncha), keyin har xabar kelgandaupdateCachedData((draft) => ...)bilan kesh jonli yangilanadi (Immerdraft), vacacheEntryRemovedorqali komponent ketganda socket yopiladi (tozalash). Bu — chat, bildirishnoma, jonli narx kabi real-time ilovalar uchun. Code generation —@rtk-query/codegen-openapibilan backend'ning OpenAPI/Swagger sxemasidan endpoint'lar va hooklar avtomatik generatsiya qilinadi (npx @rtk-query/codegen-openapi openapi-config.ts) — katta API'da yuzlab endpoint'ni qo'lda yozish o'rniga, sxemadan to'liq typed API olinadi (backend o'zgarganda qayta generatsiya). Uch nuqta: (1) retry — tarmoq xatosida avtomatik qayta urinish; (2)onCacheEntryAdded— WebSocket/SSE bilan keshni jonli yangilash; (3)codegen-openapi— sxemadan endpoint+hook avtomatik. Bu vositalar — RTK Query'ni yirik, real-time production ilovalarda to'liq quvvatga chiqaradi (Misol 17, 18).
2.16. Testlash — MSW bilan RTK Query
TESTLASH — RTK Query hook'larini MSW (Mock Service Worker) bilan sinash:
MSW — tarmoq darajasida so'rovni ushlaydi (haqiqiy fetch, soxta javob) — eng ishonchli
1. Handler (soxta endpoint):
const server = setupServer(
http.get("/api/products", () => HttpResponse.json([{ id: "1", name: "Olma" }])),
);
2. Test store (har testda yangi — kesh izolyatsiya):
function renderWithStore(ui) {
const store = configureStore({
reducer: { [productsApi.reducerPath]: productsApi.reducer },
middleware: (gd) => gd().concat(productsApi.middleware),
});
return render(<Provider store={store}>{ui}</Provider>);
}
3. Test: loading data (waitFor bilan async):
renderWithStore(<ProductsList />);
expect(screen.getByTestId("skeleton")).toBeInTheDocument(); // avval loading
expect(await screen.findByText("Olma")).toBeInTheDocument(); // keyin data (findBy = kutadi)
MSW — tarmoqni mock qiladi (fetch haqiqiy — RTK Query "haqiqiy" ishlaydi)
Har testda YANGI store — kesh testlar orasida oqib ketmaydi (izolyatsiya)Testlash — MSW bilan RTK Query — RTK Query'ga bog'liq komponentlarni ishonchli sinash usuli. RTK Query hook'lari haqiqiy tarmoq so'rovi qiladi, shuning uchun eng yaxshi yondashuv — MSW (Mock Service Worker) bilan tarmoq darajasida mock qilish (
fetchni emas, HTTP so'rovni ushlab, soxta javob qaytarish). Bu haqiqiyroq: RTK Query o'zining butun keshlash/holat mantig'ini haqiqatan ishlatadi, faqat server javobi soxta. Bosqichlar: (1) handler — soxta endpoint (http.get("/api/products", () => HttpResponse.json([...]))); (2) test store — har testda yangiconfigureStore(api reducer + middleware)Providerbilan o'raladi — bu kesh izolyatsiyasi uchun kritik (aks holda bir test keshi boshqasiga "oqib" ketadi va testlar noaniq bo'ladi); (3) test — komponent render qilinadi, avval loading holati tekshiriladi (getByTestId("skeleton")), keyinawait screen.findByText(...)bilan ma'lumot kutiladi (findBy*— element paydo bo'lguncha kutadigan async so'rov). Ikki nuqta: (1) MSW — tarmoqni mock qiladi (fetchhaqiqiy ishlaydi, RTK Query to'liq mantig'i bilan — eng ishonchli sinov); (2) har testda yangi store — kesh testlar orasida oqib ketmaydi (izolyatsiya majburiy). Bu — RTK Query komponentlarini barqaror va ishonchli sinashning standart usuli (Misol 19).
3. Sintaksis — tez ma'lumotnoma
CREATE API 2.3-bob: createApi({ reducerPath, baseQuery, tagTypes, endpoints: (b) => ({...}) })
BASE QUERY 2.3-bob: fetchBaseQuery({ baseUrl: "/api", prepareHeaders })
QUERY 2.4-bob: getX: builder.query({ query: (arg) => `/x/${arg}`, providesTags: ["X"] })
MUTATION 2.4-bob: addX: builder.mutation({ query: (b) => ({url,method:"POST",body:b}), invalidatesTags })
HOOK 2.5-bob: const {data,isLoading,error} = useGetXQuery(arg)
const [addX, {isLoading}] = useAddXMutation()
TAGS 2.7-bob: providesTags: ["Product"] + invalidatesTags: ["Product"]
HOLAT 2.8-bob: isLoading (birinchi) | isFetching (har) | data | error | refetch
MUTATION CALL 2.9-bob: await addX(data).unwrap() // try/catch bilan
LIST TAG 2.12-bob: providesTags: [...r.map(x=>({type:"X",id:x.id})), {type:"X",id:"LIST"}]
INFINITE 2.13-bob: builder.query({ serializeQueryArgs, merge, forceRefetch }) | builder.infiniteQuery
SKIP 2.14-bob: useGetXQuery(arg, { skip: !arg }) | useGetXQuery(arg ?? skipToken)
PREFETCH 2.14-bob: const prefetch = usePrefetch("getX"); onMouseEnter={() => prefetch(id)}
RETRY 2.15-bob: baseQuery: retry(fetchBaseQuery({...}), { maxRetries: 3 })
STREAMING 2.15-bob: onCacheEntryAdded(arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved })
CODEGEN 2.15-bob: npx @rtk-query/codegen-openapi openapi-config.ts
STORE: configureStore({ reducer: { [api.reducerPath]: api.reducer }, middleware: ...concat(api.middleware) })4. Batafsil kod namunalari
Misol 1 — API ta'rifi (createApi — 2.3, 2.4)
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const productsApi = createApi({
reducerPath: "productsApi",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
tagTypes: ["Product"],
endpoints: (builder) => ({
getProducts: builder.query<Product[], void>({
query: () => "/products",
providesTags: ["Product"], // kesh teg (2.7)
}),
getProductById: builder.query<Product, string>({
query: (id) => `/products/${id}`,
providesTags: (result, error, id) => [{ type: "Product", id }], // ID bo'yicha
}),
addProduct: builder.mutation<Product, Partial<Product>>({
query: (body) => ({ url: "/products", method: "POST", body }),
invalidatesTags: ["Product"], // qo'shilca — ro'yxat qayta yuklanadi (2.7)
}),
}),
});
export const { useGetProductsQuery, useGetProductByIdQuery, useAddProductMutation } = productsApi;Misol 2 — Store'ga ulash (2.3)
import { configureStore } from "@reduxjs/toolkit";
import { productsApi } from "../features/products/productsApi";
export const store = configureStore({
reducer: {
[productsApi.reducerPath]: productsApi.reducer, // api reducer
},
middleware: (getDefault) => getDefault().concat(productsApi.middleware), // api middleware (kesh)
});
// api.reducer (kesh) + api.middleware (so'rov boshqaruvi) — majburiy (2.3)Misol 3 — Query hook ishlatish (loading/error — 2.5, 2.8)
function ProductsList() {
const { data: products, isLoading, isFetching, error } = useGetProductsQuery();
if (isLoading) return <Skeleton />; // birinchi yuklanish (2.8)
if (error) return <Error message="Yuklab bo'lmadi" />;
if (!products?.length) return <Empty />;
return (
<div>
{isFetching && <small>Yangilanmoqda...</small>} {/* fonda yangilash 2.8-bob */}
<ul>{products.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
</div>
);
}
// Hech qanday useEffect/fetch/setState yo'q — RTK Query AVTOMATIK (2.5)Misol 4 — Argument bilan query (2.4, 2.6)
function ProductDetail({ id }: { id: string }) {
const { data: product, isLoading } = useGetProductByIdQuery(id); // argument (cache key — 2.6)
if (isLoading) return <Spinner />;
return <h1>{product?.name}</h1>;
}
// Har id alohida keshlanadi (getProductById(5) ≠ getProductById(7) — 2.6)
// id o'zgarsa — yangi so'rov (yoki kesh bo'lsa — keshdan)Misol 5 — Mutation (qo'shish — 2.9)
function AddProductForm() {
const [addProduct, { isLoading, isSuccess }] = useAddProductMutation();
async function handleSubmit(data: Partial<Product>) {
try {
await addProduct(data).unwrap(); // .unwrap() — Promise (try/catch — 2.9)
toast.success("Qo'shildi"); // muvaffaqiyat
// getProducts AVTOMATIK qayta yuklanadi (invalidatesTags — 2.7)
} catch (err) {
toast.error("Xatolik");
}
}
return <form onSubmit={/* ... */}>{isLoading && <Spinner />}</form>;
}
// Qo'shilgandan keyin ro'yxat avtomatik yangilanadi (invalidation — qo'lda refetch yo'q — 2.7)Misol 6 — Tags va invalidation (to'liq CRUD — 2.7)
export const usersApi = createApi({
reducerPath: "usersApi",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
tagTypes: ["User"],
endpoints: (builder) => ({
getUsers: builder.query<User[], void>({
query: () => "/users",
providesTags: (result) =>
result
? [...result.map((u) => ({ type: "User" as const, id: u.id })), "User"] // har user + umumiy
: ["User"],
}),
updateUser: builder.mutation<User, { id: string; data: Partial<User> }>({
query: ({ id, data }) => ({ url: `/users/${id}`, method: "PUT", body: data }),
invalidatesTags: (result, error, { id }) => [{ type: "User", id }], // faqat shu user
}),
deleteUser: builder.mutation<void, string>({
query: (id) => ({ url: `/users/${id}`, method: "DELETE" }),
invalidatesTags: ["User"], // butun ro'yxat
}),
}),
});
// ID bo'yicha invalidation — faqat o'zgargan user qayta yuklanadi (aniq, tejamkor — 2.7)Misol 7 — prepareHeaders (auth token — 2.10)
export const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({
baseUrl: "/api",
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token;
if (token) headers.set("Authorization", `Bearer ${token}`); // har so'rovga token (2.10)
return headers;
},
}),
endpoints: () => ({}),
});
// Har so'rovga token avtomatik (axios interceptor'ning RTK Query versiyasi — 11.15: 2.7)Misol 8 — Refresh token (custom baseQuery — 2.10)
import { fetchBaseQuery } from "@reduxjs/toolkit/query/react";
const rawBaseQuery = fetchBaseQuery({ baseUrl: "/api", prepareHeaders: /* token */ });
const baseQueryWithReauth = async (args: any, api: any, extraOptions: any) => {
let result = await rawBaseQuery(args, api, extraOptions);
if (result.error?.status === 401) { // token tugadi
const refresh = await rawBaseQuery("/auth/refresh", api, extraOptions);
if (refresh.data) {
api.dispatch(setToken((refresh.data as any).token)); // yangi token
result = await rawBaseQuery(args, api, extraOptions); // so'rovni qayta
} else {
api.dispatch(logout());
}
}
return result;
};
// createApi'da: baseQuery: baseQueryWithReauth
// 401 refresh qayta so'rov (foydalanuvchi qayta login qilmaydi — 2.10)Misol 9 — Optimistic update (2.9)
toggleLike: builder.mutation<void, string>({
query: (id) => ({ url: `/products/${id}/like`, method: "POST" }),
async onQueryStarted(id, { dispatch, queryFulfilled }) {
// 1. Darrov keshni yangila (server javobini kutmasdan):
const patch = dispatch(
productsApi.util.updateQueryData("getProducts", undefined, (draft) => {
const product = draft.find((p) => p.id === id);
if (product) product.liked = !product.liked; // UI darrov o'zgaradi
})
);
try {
await queryFulfilled; // server javobini kut
} catch {
patch.undo(); // xato rollback (2.9)
}
},
}),
// "Yoqdi" darrov (server kutmasdan); xato bo'lsa orqaga (optimistic — 2.9)Misol 10 — refetchOnFocus va polling (2.6)
// Oyna fokusga qaytganda yangilash (createApi'da):
// setupListeners(store.dispatch); + refetchOnFocus: true
function LiveData() {
const { data } = useGetStatsQuery(undefined, {
pollingInterval: 30000, // har 30s avtomatik yangilash (real-time'ga yaqin)
refetchOnFocus: true, // tab'ga qaytganda yangilash
refetchOnReconnect: true, // internet qayta ulanganda
});
return <Dashboard stats={data} />;
}
// pollingInterval — davriy yangilash (dashboard/jonli ma'lumot — 2.6)Misol 11 — Lazy query (qo'lda trigger — 2.5)
import { useLazyGetUserByIdQuery } from "./api";
function UserSearch() {
const [trigger, { data, isLoading }] = useLazyGetUserByIdQuery(); // qo'lda (avtomatik emas)
return (
<div>
<button onClick={() => trigger("5")}>Foydalanuvchini yukla</button> {/* qo'lda so'rov */}
{isLoading && <Spinner />}
{data && <p>{data.name}</p>}
</div>
);
}
// useLazyXQuery — query'ni QO'LDA ishga tushirish (mount'da emas — masalan qidiruv tugmaci)Misol 12 — Transform response (ma'lumotni o'zgartirish — 2.4)
getUsers: builder.query<User[], void>({
query: () => "/users",
transformResponse: (response: { data: User[] }) => response.data, // {data:[...]} [...]
transformErrorResponse: (error) => error.data, // xato o'zgartirish
}),
// transformResponse — server javobini komponentga qulay shaklga (masalan o'rab olingan {data})Misol 13 — Provider va setup (to'liq — 2.2)
// store.ts
import { configureStore } from "@reduxjs/toolkit";
import { setupListeners } from "@reduxjs/toolkit/query";
import { api } from "./api";
export const store = configureStore({
reducer: { [api.reducerPath]: api.reducer, auth: authReducer },
middleware: (getDefault) => getDefault().concat(api.middleware),
});
setupListeners(store.dispatch); // refetchOnFocus/Reconnect uchun (2.6)
// main.tsx
import { Provider } from "react-redux";
createRoot(document.getElementById("root")!).render(
<Provider store={store}><App /></Provider>
);Misol 14 — To'liq CRUD sahifa (RTK Query — 2.7, 2.9)
function ProductsPage() {
const { data: products, isLoading } = useGetProductsQuery();
const [addProduct] = useAddProductMutation();
const [deleteProduct] = useDeleteProductMutation();
if (isLoading) return <Skeleton />;
return (
<div>
<button onClick={() => addProduct({ name: "Yangi" })}>+ Qo'shish</button> {/* invalidate ro'yxat yangilanadi */}
<ul>
{products?.map((p) => (
<li key={p.id}>
{p.name}
<button onClick={() => deleteProduct(p.id)}></button> {/* o'chir ro'yxat yangilanadi */}
</li>
))}
</ul>
</div>
);
}
// Hech qanday qo'lda kesh/refetch yo'q — invalidatesTags hammasini avtomatik qiladi (2.7)
// Bu KOD — createAsyncThunk bilan 5 baravar uzun bo'lardi (12.2 bilan solishtir)Misol 15 — LIST tag pattern (aniq invalidation — 2.12)
export const productsApi = createApi({
reducerPath: "productsApi",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
tagTypes: ["Product"],
endpoints: (builder) => ({
getProducts: builder.query<Product[], void>({
query: () => "/products",
providesTags: (result) =>
result
? [
...result.map((p) => ({ type: "Product" as const, id: p.id })), // har element
{ type: "Product" as const, id: "LIST" }, // ro'yxatning o'zi
]
: [{ type: "Product" as const, id: "LIST" }],
}),
addProduct: builder.mutation<Product, Partial<Product>>({
query: (body) => ({ url: "/products", method: "POST", body }),
invalidatesTags: [{ type: "Product", id: "LIST" }], // faqat ro'yxat qayta (yangi element)
}),
updateProduct: builder.mutation<Product, { id: string; data: Partial<Product> }>({
query: ({ id, data }) => ({ url: `/products/${id}`, method: "PUT", body: data }),
invalidatesTags: (result, error, { id }) => [{ type: "Product", id }], // faqat shu element
}),
}),
});
// id:"LIST" — qo'shish/o'chirishda; id:p.id — bitta element o'zgarishda (aniq, tejamkor — 2.12)Misol 16 — Infinite scroll (merge — 2.13)
getFeed: builder.query<Post[], number>({
query: (page) => `/feed?page=${page}&limit=20`,
// Barcha sahifa uchun BITTA kesh (page argumentini kalitdan chiqarib tashla):
serializeQueryArgs: ({ endpointName }) => endpointName,
// Yangi sahifani mavjud keshga qo'sh (Immer draft):
merge: (currentCache, newItems) => {
currentCache.push(...newItems);
},
// Argument (page) o'zgarganda yangi so'rovga ruxsat ber:
forceRefetch: ({ currentArg, previousArg }) => currentArg !== previousArg,
}),
// Komponent:
function Feed() {
const [page, setPage] = useState(1);
const { data, isFetching } = useGetFeedQuery(page);
return (
<div>
{data?.map((post) => <Card key={post.id} post={post} />)}
<button disabled={isFetching} onClick={() => setPage((p) => p + 1)}>
Ko'proq yuklash
</button>
</div>
);
}
// merge — eski + yangi sahifa BITTA ro'yxatda (cheksiz lenta — 2.13)
// RTK Query 2.x'da builder.infiniteQuery + useGetFeedInfiniteQuery ham bor (rasmiy API)Misol 17 — Conditional fetch (skip) va prefetch (2.14)
// SKIP — shartli so'rov (argument tayyor bo'lmaguncha yubormaslik):
function Profile({ userId }: { userId?: string }) {
const { data, isLoading } = useGetProfileQuery(userId!, {
skip: !userId, // userId yo'q so'rov yuborilmaydi (2.14)
});
if (!userId) return <p>Foydalanuvchi tanlanmagan</p>;
if (isLoading) return <Spinner />;
return <h1>{data?.name}</h1>;
}
// TypeScript'da toza yo'l: useGetProfileQuery(userId ?? skipToken)
// PREFETCH — hover'da oldindan yuklash (bosganda darrov ochiladi):
function ProductRow({ product }: { product: Product }) {
const prefetch = usePrefetch("getProductById");
return (
<li onMouseEnter={() => prefetch(product.id)}> {/* hover kesh oldindan tayyor 2.14-bob */}
<Link to={`/products/${product.id}`}>{product.name}</Link>
</li>
);
}
// skip — ortiqcha/xato so'rovning oldini oladi; prefetch — silliq navigatsiya (2.14)Misol 18 — Retry va streaming (WebSocket cache update — 2.15)
import { fetchBaseQuery, retry } from "@reduxjs/toolkit/query/react";
export const chatApi = createApi({
reducerPath: "chatApi",
// Tarmoq xatosida avtomatik 3 marta qayta urinish:
baseQuery: retry(fetchBaseQuery({ baseUrl: "/api" }), { maxRetries: 3 }),
endpoints: (builder) => ({
getMessages: builder.query<Message[], string>({
query: (roomId) => `/rooms/${roomId}/messages`,
// WebSocket bilan keshni JONLI yangilash:
async onCacheEntryAdded(roomId, { updateCachedData, cacheDataLoaded, cacheEntryRemoved }) {
const ws = new WebSocket(`wss://api/rooms/${roomId}`);
try {
await cacheDataLoaded; // dastlabki HTTP javobi kelguncha kut
ws.addEventListener("message", (event) => {
const msg: Message = JSON.parse(event.data);
updateCachedData((draft) => {
draft.push(msg); // yangi xabarni keshga qo'sh (Immer)
});
});
} catch {
// cacheDataLoaded rad etilsa (komponent tez ketsa) — e'tiborsiz
}
await cacheEntryRemoved; // komponent unmount bo'lganda
ws.close(); // socket'ni yop (tozalash)
},
}),
}),
});
// retry — tarmoq nosozligida avtomatik; onCacheEntryAdded — real-time kesh (chat — 2.15)Misol 19 — Test (MSW bilan — 2.16)
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";
import { render, screen } from "@testing-library/react";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import { productsApi } from "./productsApi";
const server = setupServer(
http.get("/api/products", () => HttpResponse.json([{ id: "1", name: "Olma" }])),
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
function renderWithStore(ui: React.ReactElement) {
const store = configureStore({ // HAR testda yangi store (kesh izolyatsiya)
reducer: { [productsApi.reducerPath]: productsApi.reducer },
middleware: (gd) => gd().concat(productsApi.middleware),
});
return render(<Provider store={store}>{ui}</Provider>);
}
test("mahsulotlar ro'yxatini ko'rsatadi", async () => {
renderWithStore(<ProductsList />);
expect(screen.getByTestId("skeleton")).toBeInTheDocument(); // avval loading
expect(await screen.findByText("Olma")).toBeInTheDocument(); // keyin data (findBy — kutadi)
});
// MSW tarmoqni mock qiladi (RTK Query "haqiqiy" ishlaydi); har testda yangi store — 2.165. To'g'ri va noto'g'ri holatlar
1) Server state vositasi
createAsyncThunk + slice'da API ma'lumoti (qo'lda kesh/sync — 12.2)
RTK Query (kesh/invalidation avtomatik — 2.1)2) Kesh yangilash
mutation'dan keyin qo'lda refetch (zerikarli, unutiladi)
invalidatesTags (avtomatik qayta yuklash — 2.7)3) Loading indikator
isFetching'ni to'liq spinner uchun (kesh bor'da ham spinner — yomon UX)
isLoading (birinchi spinner) + isFetching (fonda kichik indikator — 2.8)4) Auth token
har endpoint'da qo'lda token header (takror)
prepareHeaders (markazlashgan — 2.10)5) RTK Query vs TanStack
Redux'siz loyihaga RTK Query majburlash (Redux kerak bo'ladi)
Redux loyiha RTK Query; aks holda TanStack Query (2.11)6) Tez UX
har mutation'da server kutib UI yangilash (sekin his)
optimistic update (darrov UI + rollback — 2.9)7) Invalidation kengligi
providesTags: ["Product"] — bitta element o'zgarsa BUTUN ro'yxat qayta so'raladi
LIST tag pattern — id:p.id (element) + id:"LIST" (ro'yxat) — aniq, tejamkor (2.12)8) Shartli so'rov
argument yo'q'da ham so'rov (userId undefined /users/undefined — xato)
skip yoki skipToken (argument tayyor bo'lmaguncha yubormaslik — 2.14)6. Keng tarqalgan xatolar va yechimlari
Xato 1 — An error occurred while ... middleware not added
Sababi: api.middleware store'ga qo'shilmagan 2.3-bob. Yechimi: getDefault().concat(api.middleware) (Misol 2).
Xato 2 — Mutation'dan keyin ro'yxat eskirgan
Sababi: invalidatesTags/providesTags yo'q yoki mos kelmaydi 2.7-bob. Yechimi: query providesTags, mutation invalidatesTags — bir xil teg (Misol 6).
Xato 3 — Har render yangi so'rov (cheksiz)
Sababi: query argument har render yangi obyekt (cache key o'zgaradi — 2.6). Yechimi: primitiv argument; obyekt argumentni barqaror ush (useMemo).
Xato 4 — Token yuborilmaydi
Sababi: prepareHeaders sozlanmagan 2.10-bob. Yechimi: baseQuery'da prepareHeaders bilan token qo'sh (Misol 7).
Xato 5 — refetchOnFocus ishlamaydi
Sababi: setupListeners chaqirilmagan 2.6-bob. Yechimi: setupListeners(store.dispatch) (Misol 13).
Xato 6 — Optimistic update rollback bo'lmaydi
Sababi: catchda patch.undo() yo'q 2.9-bob. Yechimi: try { await queryFulfilled } catch { patch.undo() } (Misol 9).
Xato 7 — data undefined birinchi render'da
Sababi: ma'lumot hali yuklanmagan (normal). Yechimi: isLoading tekshir; data?. (optional chaining — Misol 3).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Redux Toolkit 12.2-bob: RTK Query RTK ustiga qurilgan (kesh Redux store'da).
- Context 12.1-bob: server state — Context'da emas, RTK Query'da (12.1: 2.10).
- TanStack Query 12.4-bob: muqobil server-state vositasi (Redux'siz).
- Auth (11.15, 5.16): prepareHeaders/refresh — token boshqaruvi.
- Backend (8-QISM): RTK Query NestJS API'ga ulanadi.
- Loading holatlari (11.15: 2.8): isLoading/isFetching — 4 holat avtomatik.
- Optimistic 11.4-bob: kesh yangilash (immutable — Immer draft).
- TypeScript 11.14-bob: typed endpoints (builder.query<Result, Arg>); codegen-openapi 2.15-bob.
- Testlash 11.16-bob: MSW bilan RTK Query hook'larini sinash 2.16-bob.
- WebSocket (real-time): onCacheEntryAdded — streaming kesh yangilash 2.15-bob.
8. Eng yaxshi amaliyotlar (best practices)
- Server state RTK Query'da (createAsyncThunk emas — kesh/sync — 2.1).
- Tags bilan invalidation (providesTags + invalidatesTags — avtomatik sync — 2.7).
- ID bo'yicha invalidation (faqat o'zgargan — tejamkor — Misol 6).
- isLoading vs isFetching (birinchi spinner / fonda indikator — 2.8).
- prepareHeaders (markazlashgan token — 2.10).
- Custom baseQuery (401/refresh — avtomatik — 2.10).
- Optimistic update (tez UX — rollback bilan — 2.9).
- typed endpoints (builder.query<Result, Arg> — 11.14).
- setupListeners (refetchOnFocus/Reconnect — 2.6).
- LIST tag pattern (aniq invalidation — qo'shish/o'zgartirishni ajratish — 2.12).
- skip / skipToken (argument tayyor bo'lmaguncha so'ramaslik — 2.14).
- prefetch (hover'da oldindan — silliq navigatsiya — 2.14).
- retry (tarmoq xatosida avtomatik qayta urinish — 2.15).
- MSW bilan test (tarmoq mock, har testda yangi store — 2.16).
- codegen-openapi (katta API'da endpoint/hooklar avtomatik — 2.15).
- Redux loyiha RTK Query; aks holda TanStack 2.11-bob.
9. Amaliy loyiha: "RTK Query bilan API qatlami"
RTK Query'ni real CRUD ilovada mustahkamlash.
Maqsad
Mahsulot/foydalanuvchi boshqaruvi uchun to'liq RTK Query API qatlami — query, mutation, tags, optimistic, auth.
Talablar (requirements)
- createApi: baseQuery + tagTypes + endpoints (Misol 1).
- CRUD endpoints: getAll, getById, add, update, delete (query + mutation — 2.4).
- Tags: providesTags + invalidatesTags (ID bo'yicha — Misol 6, 2.7).
- Avtomatik hooklar: komponentlarda useGetXQuery/useXMutation 2.5-bob.
- Loading: isLoading/isFetching to'g'ri (skeleton/indikator — Misol 3, 2.8).
- Auth: prepareHeaders bilan token (Misol 7, 2.10).
- Refresh (bonus): custom baseQuery (401 refresh — Misol 8).
- Optimistic: kamida bitta mutation (like/toggle — Misol 9, 2.9).
- Polling/focus: dashboard'da refetchOnFocus yoki polling (Misol 10).
- Store: api.reducer + api.middleware + setupListeners (Misol 2, 13).
- LIST tag pattern: ro'yxatda id:p.id + id:"LIST" (aniq invalidation — Misol 15, 2.12).
- Skip yoki prefetch: kamida bittasi (shartli so'rov / hover'da oldindan — Misol 17, 2.14).
- Test (bonus): kamida bitta hook MSW bilan sinaladi (Misol 19, 2.16).
Maslahatlar (hint)
- api.middleware'ni store'ga qo'shishni unutma (Xato 1).
- Tags — query provides, mutation invalidates (bir xil teg — Misol 6).
- ID bo'yicha invalidation — faqat o'zgargan yangilanadi (tejamkor).
- Token — prepareHeaders (har endpoint'da qo'lda emas — 2.10).
- Optimistic — onQueryStarted + patch.undo (rollback — Misol 9).
- createAsyncThunk 12.2-bob bilan solishtir — RTK Query qancha kam kod (Misol 14).
"Tayyor" mezonlari (acceptance criteria)
- createApi + CRUD endpoints.
- Tags (providesTags/invalidatesTags — ID bo'yicha).
- Avtomatik hooklar komponentlarda.
- isLoading/isFetching to'g'ri.
- prepareHeaders (token).
- Kamida bitta optimistic update (rollback bilan).
- Polling yoki refetchOnFocus.
- Store to'g'ri (middleware + setupListeners).
- Mutation'dan keyin kesh avtomatik yangilanadi (qo'lda refetch yo'q).
- LIST tag pattern (id:p.id + id:"LIST").
- Skip yoki prefetch kamida bitta joyda.
- (Bonus) Kamida bitta hook MSW bilan testlangan.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda RTK Query'ni chuqur o'rgandik:
- Server state muammosi (client vs server — 2.1); RTK Query (RTK ustiga — 2.2); createApi/baseQuery 2.3-bob; endpoints (query/mutation — 2.4); avtomatik hooklar 2.5-bob.
- Keshlash (cache key, staleness — 2.6); tags/invalidation (avtomatik sync — 2.7); loading holatlari (isLoading/isFetching — 2.8); mutations/optimistic 2.9-bob.
- baseQuery sozlash (auth/refresh — 2.10); RTK Query vs createAsyncThunk vs TanStack 2.11-bob.
- LIST tag pattern (aniq invalidation — 2.12); pagination/infinite scroll (merge/infiniteQuery — 2.13); conditional fetch/prefetch (skip/skipToken/usePrefetch — 2.14).
- retry/streaming/codegen (avtomatik urinish, WebSocket kesh, OpenAPI — 2.15); testlash (MSW + izolyatsiya qilingan store — 2.16).
Endi siz server ma'lumotini RTK Query bilan professional — kesh, invalidation, optimistic, auth — boshqara olasiz, va createAsyncThunk'ning qo'lda kodidan qutulasiz. Eng muhimi — server state'ni client state'dan ajratishni bilasiz.
Keyingi bob — 12.4-bob: TanStack Query (React Query). RTK Query Redux ekotizimida zo'r, lekin u Redux'ga bog'liq. TanStack Query (eski nomi React Query) — server-state boshqaruvining mustaqil, universal yechimi (Redux kerak emas), va bugun eng keng tavsiya etiladigan vosita. U RTK Query bilan bir xil muammoni (kesh, sync, loading) hal qiladi, lekin ko'proq moslashuvchan, kattaroq ekotizim, va ajoyib DevTools bilan. useQuery, useMutation, query keys, invalidation, optimistic update, infinite queries — barchasini chuqur o'rganamiz. Bu — har zamonaviy React loyihada (Redux bo'lsin yoki bo'lmasin) ishlatiladigan vosita.
Foydalanilgan rasmiy/ishonchli manbalar
- Redux Toolkit rasmiy hujjati — RTK Query bo'limi: "RTK Query Overview", "Why RTK Query?", "Comparison" (boshqa data-fetching vositalari bilan)
- Redux Toolkit — RTK Query "Queries" va "Mutations" qo'llanmalari (query/mutation endpoint'lari, hooklar,
unwrap) - Redux Toolkit — RTK Query "Cache Behavior" va "Automated Re-fetching" (cache key,
keepUnusedDataFor,refetchOnMountOrArgChange/OnFocus/OnReconnect, tag-based invalidation, LIST tag pattern) - Redux Toolkit — RTK Query "Manual Cache Updates" va "Optimistic Updates" (
updateQueryData,onQueryStarted,patch.undo) - Redux Toolkit — RTK Query "Customizing Queries":
baseQuery(prepareHeaders),retry, custom baseQuery bilan re-authentication (401 refresh) - Redux Toolkit — RTK Query "Streaming Updates" (
onCacheEntryAdded, WebSocket bilan kesh yangilash) va "Pagination"/infiniteQuery - Redux Toolkit — RTK Query "Prefetching" (
usePrefetch), "Conditional Fetching" (skip,skipToken), "Code Generation" (@rtk-query/codegen-openapi) - Redux Toolkit — RTK Query "Usage With TypeScript" va "Testing" (MSW bilan)
- TkDodo va Mark Erikson (Redux maintainer) — server state caching va RTK Query vs React Query taqqoslash maqolalari (12.4 bilan bog'liq)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!