WisarWisar
Dasturlash kitobi/12-QISM — State Management44 daqiqa

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. createAsyncThunk 12.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)

text
  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 AVTOMATIK

Server 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

text
  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 AVTOMATIK

RTK Query nima va arxitekturasi — Redux Toolkit'ning server-state yechimi (@reduxjs/toolkit ichida keladi — alohida o'rnatish kerak emas). Arxitektura: (1) createApi — API "ta'rifi" (barcha endpoint'lar + baseQuery — asosiy so'rov mexanizmi); (2) endpointsquery (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

text
  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)

createApi va baseQuery — 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 bitta api (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

text
  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 bilan getProductById: 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 }) })query obyekt 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) query funksiyasi 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

text
  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): getUsers useGetUsersQuery, addUser useAddUserMutation. 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 bilan useGetUserByIdQuery(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); mutationtrigger funksiya bilan (foydalanuvchi harakati natijasida). Bu — server bilan ishlashni minimal kodga (deyarli faqat hook chaqirish) qisqartiradi.

2.6. Keshlash va cache hayot sikli

text
  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 komponent useGetUsersQuery() 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

text
  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. Yechimtags (teglar): (1) createApi'da tagTypes: ["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: addProduct chaqirilganda "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'lda refetch chaqirish 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

text
  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). isLoading vs isFetching (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 (isLoading false), va isFetching orqali 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

text
  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(), keyin await 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 bu onQueryStarted bilan amalga oshiriladi: (1) darrov keshni yangilash (dispatch(api.util.updateQueryData(...)) — UI darrov o'zgaradi); (2) await queryFulfilled (server javobini kut); (3) catch ichida patch.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/xatoni try/catch bilan 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

text
  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)

baseQuery sozlash — 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

text
  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. createAsyncThunk 12.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 uchun createAsyncThunk emas (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

text
  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) faqat id: "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'chirish id: "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

text
  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

text
  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 userId yuklanmagan) yoki foydalanuvchi login qilmagan — bunda so'rov yubormaslik kerak. useGetProfileQuery(userId, { skip: !userId })skip true bo'lsa so'rov yuborilmaydi (isLoading false qoladi). TypeScript'da toza yo'l — skipToken: useGetProfileQuery(userId ?? skipToken) (bu data turini ham to'g'ri boshqaradi). Prefetch — ma'lumotni oldindan yuklash (foydalanuvchi bosishidan oldin): const prefetch = usePrefetch("getProductById"), keyin onMouseEnter={() => 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

text
  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 darajasida extraOptions: { 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: onCacheEntryAdded lifecycle'i ichida WebSocket ochiladi, cacheDataLoaded kutiladi (dastlabki HTTP ma'lumoti yuklanguncha), keyin har xabar kelganda updateCachedData((draft) => ...) bilan kesh jonli yangilanadi (Immer draft), va cacheEntryRemoved orqali komponent ketganda socket yopiladi (tozalash). Bu — chat, bildirishnoma, jonli narx kabi real-time ilovalar uchun. Code generation@rtk-query/codegen-openapi bilan 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

text
  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 yangi configureStore (api reducer + middleware) Provider bilan 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")), keyin await screen.findByText(...) bilan ma'lumot kutiladi (findBy* — element paydo bo'lguncha kutadigan async so'rov). Ikki nuqta: (1) MSW — tarmoqni mock qiladi (fetch haqiqiy 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

text
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)

ts
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)

ts
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)

tsx
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)

tsx
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)

tsx
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)

ts
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)

ts
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)

ts
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)

ts
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)

tsx
// 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)

tsx
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)

ts
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)

tsx
// 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)

tsx
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)

ts
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)

ts
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)

tsx
// 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)

ts
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)

tsx
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.16

5. To'g'ri va noto'g'ri holatlar

1) Server state vositasi

text
 createAsyncThunk + slice'da API ma'lumoti (qo'lda kesh/sync — 12.2)
 RTK Query (kesh/invalidation avtomatik — 2.1)

2) Kesh yangilash

text
 mutation'dan keyin qo'lda refetch (zerikarli, unutiladi)
 invalidatesTags (avtomatik qayta yuklash — 2.7)

3) Loading indikator

text
 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

text
 har endpoint'da qo'lda token header (takror)
 prepareHeaders (markazlashgan — 2.10)

5) RTK Query vs TanStack

text
 Redux'siz loyihaga RTK Query majburlash (Redux kerak bo'ladi)
 Redux loyiha  RTK Query; aks holda  TanStack Query (2.11)

6) Tez UX

text
 har mutation'da server kutib UI yangilash (sekin his)
 optimistic update (darrov UI + rollback — 2.9)

7) Invalidation kengligi

text
 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

text
 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)

  1. createApi: baseQuery + tagTypes + endpoints (Misol 1).
  2. CRUD endpoints: getAll, getById, add, update, delete (query + mutation — 2.4).
  3. Tags: providesTags + invalidatesTags (ID bo'yicha — Misol 6, 2.7).
  4. Avtomatik hooklar: komponentlarda useGetXQuery/useXMutation 2.5-bob.
  5. Loading: isLoading/isFetching to'g'ri (skeleton/indikator — Misol 3, 2.8).
  6. Auth: prepareHeaders bilan token (Misol 7, 2.10).
  7. Refresh (bonus): custom baseQuery (401 refresh — Misol 8).
  8. Optimistic: kamida bitta mutation (like/toggle — Misol 9, 2.9).
  9. Polling/focus: dashboard'da refetchOnFocus yoki polling (Misol 10).
  10. Store: api.reducer + api.middleware + setupListeners (Misol 2, 13).
  11. LIST tag pattern: ro'yxatda id:p.id + id:"LIST" (aniq invalidation — Misol 15, 2.12).
  12. Skip yoki prefetch: kamida bittasi (shartli so'rov / hover'da oldindan — Misol 17, 2.14).
  13. 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!
12.3-bob: RTK Query — Wisar