WisarWisar
Dasturlash kitobi/12-QISM — State Management41 daqiqa

12.4-bob: TanStack Query (React Query)

12-QISM — State Management va Data Fetching · 4-mavzu


1. Kirish va motivatsiya

12.3-bobda RTK Query bilan server-state'ni boshqardik — kesh, invalidation, optimistic update. Lekin RTK Query'ning bir cheklovi bor: u Redux'ga bog'liq (RTK ekotizimida yashaydi). Agar loyihangizda Redux bo'lmasa (yoki siz Redux ishlatmoqchi bo'lmasangiz), RTK Query uchun Redux'ni qo'shishga majbur bo'lasiz. Aynan shu yerda TanStack Query (eski va keng tanilgan nomi — React Query) keladi: u server-state boshqaruvining mustaqil, universal yechimi — Redux yoki boshqa hech narsaga bog'liq emas, faqat React (va aslida framework'dan mustaqil — Vue, Solid, Svelte versiyalari ham bor). Va eng muhimi — bugun TanStack Query eng keng tavsiya etiladigan va eng ko'p ishlatiladigan server-state vositasi.

TanStack Query va RTK Query bir xil falsafaga asoslanadi (server state — client state'dan alohida — 12.1: 2.10, 12.3: 2.1), bir xil muammolarni (kesh, sync, loading, deduplication, fonda yangilash) hal qiladi. Farq — yondashuv va ekotizimda: RTK Query deklarativ (endpoint'larni oldindan ta'riflaysiz, hooklar avtomatik), TanStack Query imperativroq va moslashuvchanroq (useQueryda to'g'ridan query function berasiz — istalgan fetch/axios bilan). TanStack Query'ning kattaroq community'si, ko'proq funksiyasi (infinite queries, parallel/dependent queries, optimistic — ancha qulay), va ajoyib DevTools'i bor. Ko'p dasturchi uni "frontend'ning eng yaxshi kutubxonasi" deb ataydi — chunki u server bilan ishlashni tubdan soddalashtiradi.

Bu bob: TanStack Query nima (mustaqil server-state — RTK Query bilan taqqos), setup (QueryClient, QueryClientProvider), useQuery (queryKey, queryFn), query hayot sikli va keshlash (staleTime, gcTime), loading/error holatlari (isPending/isFetching/error), query keys (struktura, dependency), dependent va parallel queries, useMutation (server o'zgartirish), invalidation va cache update (kesh sinxronlash), optimistic updates, pagination va infinite queries, prefetching va DevTools, va RTK Query vs TanStack Query (yakuniy qaror). Uni to'liq — server-state falsafasi va amaliy holatda — ochamiz.

O'xshatish: TanStack Query — bu shaxsiy yordamchi (assistant) bilan kutubxona. Oddiy fetch (useEffect+fetch — 11.5) — bu o'zingiz kutubxonaga borib, kitobni qidirib, olib kelib, o'qib, joyiga qaytarish (har gal, qo'lda, takror). TanStack Query — bu aqlli yordamchi: "menga falon kitob kerak" desangiz (useQuery), u kitobni keltiradi va eslab qoladi (kesh) — keyingi safar so'rasangiz darrov beradi. Kitob eskirsa (yangi nashr chiqsa — staleness), u fonda yangisini keltiradi (siz eskisini o'qib turgan paytda). Bir nechta odam bir kitobni so'rasa — u bir marta olib, hammaga beradi (deduplication). Siz kitobni o'zgartirsangiz (mutation — masalan izoh qo'shsangiz), u tegishli kitoblarning nusxalarini yangilaydi (invalidation). Siz faqat "nima kerak"ni aytasiz — qolganini aqlli yordamchi (TanStack Query) qiladi.

Nega muhim?

  • Eng keng server-state vositasi — bugun React loyihalarining katta qismi TanStack Query ishlatadi (standart).
  • Mustaqil, universal — Redux yoki boshqa hech narsa kerak emas (faqat o'zi — har loyihada).
  • Zamonaviy stack'ning tarkibiy qismi — bugungi React ekotizimida server-state uchun birinchi tavsiya.
  • Tub soddalashtirishuseEffect+fetch'ning (loading/error/kesh/race) butun og'rig'ini yo'qotadi.

2. Nazariya — chuqur tushuntirish

2.1. TanStack Query nima (RTK Query bilan taqqos)

text
  TANSTACK QUERY — MUSTAQIL server-state (data fetching + caching) kutubxonasi:
  npm install @tanstack/react-query
  (Redux KERAK EMAS — har loyihada, framework'dan mustaqil)

  RTK QUERY vs TANSTACK QUERY (ikkalasi server state — farq):
  ┌──────────────────┬──────────────────────┬──────────────────────┐
  │                  │ RTK Query 12.3-bob     │ TanStack Query (bu bob)│
  ├──────────────────┼──────────────────────┼──────────────────────┤
  │ Bog'liqlik       │ Redux KERAK          │ MUSTAQIL (hech narsa) │
  │ Yondashuv        │ deklarativ (endpoint)│ imperativ (queryFn)   │
  │ Setup            │ createApi (oldindan) │ useQuery (joyida)     │
  │ Moslashuvchanlik │ tartibli, Redux ichida│ ko'proq erkin/keng    │
  │ Community        │ Redux ekotizimi      │ ENG KATTA (standart)  │
  └──────────────────┴──────────────────────┴──────────────────────┘

   Ikkalasi bir muammoni (server state) hal qiladi — farq ekotizim va uslubda
   Redux loyiha  RTK Query (tabiiy); aks holda  TanStack Query (ko'pincha afzal)

TanStack Query nima — mustaqil server-state kutubxonasi (@tanstack/react-query). Eski nomi React Query edi (4-versiyadan "TanStack Query" — chunki Vue, Solid, Svelte versiyalari ham chiqdi — framework'dan mustaqil yadro). Eng muhim — u Redux'ga (yoki boshqa hech narsaga) bog'liq emas — har loyihada mustaqil ishlaydi. RTK Query bilan taqqos: ikkalasi ham server state'ni (kesh, sync, loading) hal qiladi, lekin farq bor: (1) bog'liqlik — RTK Query Redux kerak, TanStack Query mustaqil; (2) yondashuv — RTK Query deklarativ (endpoint'larni oldindan createApida ta'riflaysiz), TanStack Query imperativroq (useQueryda to'g'ridan query function berasiz — joyida); (3) moslashuvchanlik — TanStack Query ko'proq erkin va keng; (4) community — TanStack Query'ning community'si eng katta (de-facto standart). Ikki nuqta: (1) ikkalasi bir muammoni (server state — 12.3: 2.1) hal qiladi — farq ekotizim va uslubda; (2) Redux loyiha RTK Query (tabiiy, qo'shimcha kutubxona kerak emas); aks holda (Redux'siz, yangi loyiha) TanStack Query (ko'pincha afzal — eng keng, moslashuvchan). Bu bobda TanStack Query'ni chuqur o'rganamiz, chunki u zamonaviy React'ning de-facto server-state standarti.

2.2. Setup — QueryClient va QueryClientProvider

text
  TANSTACK QUERY SETUP (2 qadam):

  1. QueryClient — kesh va sozlamalar "miyasi" (bir marta yaratiladi):
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000,        // 1 daqiqa "yangi" (eskirish — 2.4)
        retry: 1,                     // xato  1 marta qayta urinish
      },
    },
  });

  2. QueryClientProvider — ilovani o'raydi (Context kabi):
  <QueryClientProvider client={queryClient}>
    <App />
    <ReactQueryDevtools />            // DevTools (kesh ko'rinishi — 2.12)
  </QueryClientProvider>

  ┌────────────────────────────────────────────────────────────┐
  │ QueryClient: kesh miyasi (sozlama) | Provider: ilovaga beradi│
  └────────────────────────────────────────────────────────────┘

   QueryClient — bir marta (kesh saqlanadi); Provider — ildizda (Context kabi)
   defaultOptions — global sozlama (staleTime, retry — har query'ga — 2.4)

Setup — QueryClient va QueryClientProvider — TanStack Query'ni ishga tushirish (ikki qadam). (1) QueryClient — kesh va global sozlamalarning "miyasi" (bir marta yaratiladi): new QueryClient({ defaultOptions: { queries: { staleTime: 60000, retry: 1 } } })defaultOptions barcha query'ga qo'llanadigan global sozlama (staleTime — ma'lumot qancha vaqt "yangi" hisoblanadi — 2.4; retry — xato bo'lganda necha marta qayta urinish). (2) QueryClientProvider — ilovani o'rab, QueryClient'ni beradi (Context kabi — 12.1): <QueryClientProvider client={queryClient}><App /></QueryClientProvider>, va odatda <ReactQueryDevtools /> ham qo'shiladi (kesh holatini ko'rish — 2.12). Ikki nuqta: (1) QueryClientbir marta yaratiladi (komponent tashqarida yoki useState/useRefda — har render qayta yaratilmacligi uchun; kesh shu obyektda saqlanadi); Provider — ildizda (main.tsx/App, Context Provider kabi); (2) defaultOptions — global sozlama (har query'ga, lekin har query o'zida ham override qila oladi — 2.4). Bu — TanStack Query'ning minimal, bir martalik sozlamasi (keyin faqat useQuery/useMutation ishlatasiz).

2.3. useQuery — queryKey va queryFn

text
  useQuery — ma'lumot O'QISH (eng asosiy hook):

  import { useQuery } from "@tanstack/react-query";

  const { data, isPending, error } = useQuery({
    queryKey: ["products"],                  // KESH KALITI (unikal — 2.6)
    queryFn: () => fetch("/api/products").then((r) => r.json()),   // SO'ROV funksiyasi
  });

  IKKI ASOSIY QISM:
  queryKey — keshni identifikatsiya qiladi (massiv — ["products"], ["product", id])
              har xil key — har xil kesh; bir xil key — ulashilgan kesh (dedupe)
  queryFn  — ma'lumotni qaytaruvchi async funksiya (fetch, axios — ISTALGAN)
              Promise qaytaradi (data — uning natijasi)

  ARGUMENT bilan (queryKey'da + queryFn'da):
  const { data } = useQuery({
    queryKey: ["product", id],               // id key'ning bir qismi (id o'zgarsa — yangi kesh)
    queryFn: () => fetch(`/api/products/${id}`).then((r) => r.json()),
  });

   queryKey — kesh kaliti (massiv); queryFn — so'rov (istalgan fetch/axios)
   queryFn — faqat Promise qaytarsa yetadi (fetch, axios, GraphQL — universal)

useQuery — queryKey va queryFn — TanStack Query'ning eng asosiy hook'i (ma'lumot o'qish). useQuery({ queryKey, queryFn }) — ikki asosiy qism: (1) queryKey — keshni identifikatsiya qiluvchi massiv (["products"], ["product", id]) — bu kesh kaliti: har xil key — har xil kesh, bir xil key — ulashilgan kesh (deduplication — bir nechta komponent bir xil query'ni ishlatsa, bitta so'rov); (2) queryFn — ma'lumotni qaytaruvchi async funksiya (() => fetch("/api/products").then(r => r.json())) — u Promise qaytarsa yetadi, shuning uchun istalgan so'rov mexanizmi (fetch, axios — 2.18, GraphQL) ishlatsa bo'ladi (RTK Query'ning fetchBaseQuery'sidan moslashuvchanroq). Argument bilan: queryKey: ["product", id] (id key'ning bir qismi — id o'zgarsa yangi kesh, yangi so'rov), queryFn: () => fetch(\/api/products/${id}`). Ikki nuqta: (1) **queryKey** — kesh kaliti (massiv — strukturali, dependency bilan — 2.6); (2) **queryFn** — so'rov funksiyasi (Promise qaytarsa yetadi — universal, har qanday fetch/axios/GraphQL bilan). useQueryuseEffect`+fetch'ning 11.5-bob butun og'rig'ini (loading, error, kesh, race, dedupe) bir necha qatorga qisqartiradi.

2.4. Query hayot sikli va keshlash (staleTime, gcTime)

text
  IKKI MUHIM VAQT (TanStack Query keshining asosi):

  staleTime — ma'lumot qancha vaqt "YANGI" (fresh) hisoblanadi (default: 0):
  - "yangi" davrida  keshdan (so'rov YO'Q)
  - "eskirgan" (stale) bo'lgach  keshdan KO'RSATADI + fonda YANGILAydi (refetch)

  gcTime (eski nomi cacheTime) — ishlatilmagan kesh qancha SAQLANADI (default: 5 daqiqa):
  - hech bir komponent query'ni ishlatmasa  gcTime'dan keyin kesh O'CHIRILADI

  HAYOT SIKLI:
  fetch ──► fresh (staleTime) ──► stale (eskirgan) ──► [unmount] ──► gcTime ──► o'chirish
            (keshdan)            (keshdan+fonda       (kesh saqlanadi)
                                  yangilash)

  STALE-WHILE-REVALIDATE (asosiy strategiya):
   eskirgan ma'lumotni DARROV ko'rsatish (keshdan) + fonda yangisini olish
   foydalanuvchi kutmaydi (eski ko'radi), keyin yangilanadi (eng yaxshi UX)

   staleTime — qancha "yangi" (so'rov yo'q); gcTime — ishlatilmagan kesh saqlanishi
   Stale-while-revalidate — eski + fonda yangi (foydalanuvchi hech kutmaydi)

Query hayot sikli va keshlash — TanStack Query keshining ikki muhim vaqti. staleTime — ma'lumot qancha vaqt "yangi" (fresh) hisoblanishi (default: 0 — darrov eskiradi): ma'lumot "yangi" davrida keshdan olinadi (so'rov yuborilmaydi); "eskirgan" (stale) bo'lgach — keshdan ko'rsatiladi (darrov), lekin fonda yangisi olinadi (refetch). gcTime (garbage collection time, eski nomi cacheTime) — ishlatilmagan kesh qancha vaqt saqlanishi (default: 5 daqiqa): agar hech bir komponent query'ni ishlatmasa, gcTime'dan keyin kesh o'chiriladi. Hayot sikli: fetch fresh (staleTime davomida keshdan) stale (eskirgan — keshdan + fonda yangilash) [komponent unmount] gcTime davomida saqlanadi o'chirish. Eng muhim strategiya — "stale-while-revalidate": eskirgan ma'lumotni darrov ko'rsatish (keshdan) va fonda yangisini olish — foydalanuvchi hech kutmaydi (eski ma'lumotni ko'radi, keyin jim yangilanadi). Ikki nuqta: (1) staleTime — qancha "yangi" (bu davrda so'rov yo'q — performance); gcTime — ishlatilmagan kesh qancha saqlanishi; (2) stale-while-revalidate — eski + fonda yangi (eng yaxshi UX, hech "yuklanmoqda" oq ekran yo'q kesh bor bo'lganda). staleTime'ni to'g'ri qo'yish (masalan 5 daqiqa kam o'zgaradigan ma'lumot uchun) — keraksiz so'rovlarni keskin kamaytiradi.

2.5. Loading va error holatlari

text
  useQuery HOLATLARI (status):
  isPending  — yuklanmoqda (hali ma'lumot yo'q — v5'da "isLoading"  "isPending")
  isError    — xato (error bor)
  isSuccess  — muvaffaqiyat (data bor)
  isFetching — har QANDAY yuklanish (birinchi + fonda — RTK Query'dek — 12.3: 2.8)
  data       — ma'lumot
  error      — xato obyekti
  refetch    — qo'lda qayta yuklash

  function Products() {
    const { data, isPending, isError, error, isFetching } = useQuery({
      queryKey: ["products"],
      queryFn: fetchProducts,
    });
    if (isPending) return <Skeleton />;           // birinchi yuklanish
    if (isError) return <Error message={error.message} />;
    return (
      <div>
        {isFetching && <small>Yangilanmoqda...</small>}   {/* fonda 2.4-bob */}
        <List items={data} />
      </div>
    );
  }

   isPending — birinchi (kesh yo'q); isFetching — har yuklash (fonda — 12.3: 2.8 kabi)
   v5: "isLoading"  "isPending" (kesh yo'q + fetch bo'layotgan)

Loading va error holatlariuseQueryning status holatlari (RTK Query'dek — 12.3: 2.8). Asosiy holatlar: isPending (yuklanmoqda — hali ma'lumot yo'q; TanStack Query v5'da eski isLoading isPendingga o'zgardi), isError (xato), isSuccess (muvaffaqiyat), isFetching (har qanday yuklanish — birinchi + fonda yangilash), data (ma'lumot), error (xato obyekti), refetch (qo'lda qayta yuklash). Naqsh: if (isPending) return <Skeleton/> (birinchi yuklanish), if (isError) return <Error/>, va {isFetching && <small>Yangilanmoqda...</small>} (fonda yangilash indikatori — 2.4). Ikki nuqta: (1) isPending — birinchi yuklanish (kesh yo'q — to'liq skeleton); isFetching — har yuklash (fonda — kichik indikator — 12.3: 2.8 bilan bir xil g'oya); (2) v5 o'zgarishi — eski isLoading endi isPending (kesh yo'q + fetch bo'layotgan holat); isLoading hali bor lekin isPending && isFetchingga teng. Bu holatlar 11.15: 2.8 dagi 4 holatni (loading/error/empty/data) avtomatik boshqaradi — useEffect+fetch'dagi qo'lda useState (loading/error/data) butunlay yo'qoladi.

2.6. Query keys — struktura va dependency

text
  QUERY KEY — keshni identifikatsiya qiluvchi MASSIV (struktura muhim):

  ODDIY  MURAKKAB (umumiydan aniqga):
  ["products"]                          — barcha mahsulotlar
  ["products", { category: "phone" }]   — filtrlangan (obyekt — argument)
  ["products", productId]               — bitta mahsulot
  ["products", productId, "reviews"]    — mahsulotning sharhlari (ichma-ich)

  DEPENDENCY (key argument o'zgarsa — yangi so'rov):
  const { data } = useQuery({
    queryKey: ["products", category, page],   // category yoki page o'zgarsa — yangi so'rov
    queryFn: () => fetchProducts(category, page),
  });
  //  category "phone""laptop" — yangi kesh, yangi so'rov (yoki kesh bo'lsa keshdan)

  KONVENSIYA (Query Key Factory — tartibli):
  const productKeys = {
    all: ["products"],
    list: (filters) => [...productKeys.all, "list", filters],
    detail: (id) => [...productKeys.all, "detail", id],
  };

   Query key — MASSIV (umumiydan aniqga — invalidation uchun ierarxiya — 2.9)
   Key'dagi o'zgaruvchi (id/filtr) — dependency (o'zgarsa — yangi so'rov, avtomatik)

Query keys — struktura va dependency — TanStack Query'ning markaziy tushunchasi (kesh va invalidation key'ga tayanadi). Query key — keshni identifikatsiya qiluvchi massiv; struktura muhim (umumiydan aniqga): ["products"] (barcha), ["products", { category: "phone" }] (filtrlangan — obyekt argument), ["products", productId] (bitta), ["products", productId, "reviews"] (ichma-ich — sharhlar). Dependency — key'dagi o'zgaruvchi o'zgarsa, TanStack Query avtomatik yangi so'rov yuboradi: queryKey: ["products", category, page]category yoki page o'zgarsa, yangi kesh + yangi so'rov (yoki o'sha key kesh'i bo'lsa keshdan). Bu — useEffect dependency'siga o'xshaydi, lekin avtomatik (key o'zgarsa query qayta ishlaydi). KonvensiyaQuery Key Factory (tartibli, xatosiz): const productKeys = { all: ["products"], list: (f) => [...productKeys.all, "list", f], detail: (id) => [...productKeys.all, "detail", id] } — key'larni markazlashgan obyektda saqlash (qo'lda key yozish o'rniga — typo va invalidation xatolaridan saqlaydi). Ikki nuqta: (1) query key — massiv (umumiydan aniqga — ierarxik invalidation uchun — ["products"] ni bekor qilsa, barcha ["products", ...] ham bekor — 2.9); (2) key'dagi o'zgaruvchi — dependency (o'zgarsa avtomatik yangi so'rov — filtr, sahifa, id). Key — TanStack Query'ni to'g'ri ishlatishning asosi.

2.7. Dependent va parallel queries

text
  PARALLEL QUERIES — bir nechta query BIR VAQTDA (mustaqil — birga yuklanadi):
  const users = useQuery({ queryKey: ["users"], queryFn: fetchUsers });
  const posts = useQuery({ queryKey: ["posts"], queryFn: fetchPosts });
  //  ikkalasi PARALLEL yuklanadi (bir-birini kutmaydi — tez)

  // Dinamik parallel (massiv) — useQueries:
  const results = useQueries({
    queries: userIds.map((id) => ({ queryKey: ["user", id], queryFn: () => fetchUser(id) })),
  });

  DEPENDENT QUERIES — biri ikkinchisiga BOG'LIQ (ketma-ket — enabled):
  const { data: user } = useQuery({ queryKey: ["user", email], queryFn: () => fetchUser(email) });
  const { data: projects } = useQuery({
    queryKey: ["projects", user?.id],
    queryFn: () => fetchProjects(user.id),
    enabled: !!user?.id,                  //  user kelguncha KUTadi (user bo'lganda ishlaydi)
  });
  //  avval user, keyin uning projects'i (ketma-ket — bog'liq)

   Parallel — mustaqil query'lar birga (tez); useQueries — dinamik massiv
   Dependent — enabled bilan (oldingi natija kelguncha kutadi — ketma-ket)

Dependent va parallel queries — bir nechta query'ni boshqarish. Parallel queries — bir nechta mustaqil query bir vaqtda (birga) yuklanadi: const users = useQuery({...}); const posts = useQuery({...}) — ikkalasi parallel (bir-birini kutmaydi — eng tez). Dinamik parallel (soni o'zgaruvchi) uchun useQueries: useQueries({ queries: userIds.map(id => ({ queryKey: ["user", id], queryFn: () => fetchUser(id) })) }) — massivdagi har id uchun query. Dependent (ketma-ket) queries — biri ikkinchisiga bog'liq (oldingi natija kerak): const { data: user } = useQuery({...}); const { data: projects } = useQuery({ queryKey: ["projects", user?.id], queryFn: () => fetchProjects(user.id), enabled: !!user?.id })enabled opsiyasi: user kelguncha ikkinchi query kutadi (!!user?.id false bo'lganda query ishlamaydi, user kelgach — ishga tushadi). Ikki nuqta: (1) parallel — mustaqil query'lar birga (tez — bir-birini kutmaydi); dinamik soni uchun useQueries; (2) dependentenabled bilan (oldingi natija kelguncha kutadi — masalan avval foydalanuvchini olib, keyin uning ma'lumotlarini). enabled — query'ni shartli ishga tushirish uchun ham ishlatiladi (masalan modal ochiq bo'lganda — enabled: isOpen). Bu — murakkab ma'lumot bog'liqliklarini boshqarishning toza usuli.

2.8. useMutation — server o'zgartirish

text
  useMutation — server ma'lumotini O'ZGARTIRISH (POST/PUT/DELETE):

  const mutation = useMutation({
    mutationFn: (newProduct) => fetch("/api/products", {
      method: "POST", body: JSON.stringify(newProduct),
    }).then((r) => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["products"] });   // kesh yangilash 2.9-bob
      toast.success("Qo'shildi");
    },
    onError: (error) => toast.error(error.message),
  });

  // Trigger:
  mutation.mutate(newProduct);              // so'rov yuborish
  // yoki: await mutation.mutateAsync(newProduct);   // Promise (try/catch)

  HOLATLAR:
  mutation.isPending  — yuborilmoqda (tugma disable)
  mutation.isSuccess  — muvaffaqiyat
  mutation.isError    — xato
  mutation.mutate     — trigger funksiya

   mutationFn — o'zgartirish so'rovi; onSuccess/onError — natijaga reaksiya
   mutate (trigger) — foydalanuvchi harakatida (tugma); query — avtomatik (mount'da)

useMutation — server o'zgartirish — ma'lumotni o'zgartirish (POST/PUT/DELETE). useMutation({ mutationFn, onSuccess, onError }): (1) mutationFn — o'zgartirish so'rovi ((newProduct) => fetch("/api/products", { method: "POST", body })); (2) onSuccess — muvaffaqiyat natijasiga reaksiya (odatda keshni yangilash — queryClient.invalidateQueries(...) — 2.9, va toast); (3) onError — xatoga reaksiya. Trigger: mutation.mutate(newProduct) (so'rov yuborish — foydalanuvchi tugmani bosganda), yoki await mutation.mutateAsync(newProduct) (Promise sifatida — try/catch bilan). Holatlar: mutation.isPending (yuborilmoqda — tugmani disable qilish), isSuccess, isError, mutate (trigger). Ikki nuqta: (1) mutationFn — o'zgartirish so'rovi; onSuccess/onError — natijaga reaksiya (kesh yangilash, navigatsiya, toast); (2) query avtomatik ishlaydi (komponent mount'da), mutation trigger bilan (mutate — foydalanuvchi harakatida). useMutation — server'ni o'zgartirish va undan keyin keshni sinxron tutishning standart usuli (12.3 RTK Query mutation bilan bir xil g'oya, lekin imperativroq).

2.9. Invalidation va cache update

text
  MUTATION'dan keyin keshni yangilash — 2 usul:

  USUL 1 — INVALIDATE (eng keng — query'ni "eskirgan" deb belgila  qayta yuklanadi):
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["products"] });   // ["products"]* qayta yuklanadi
  }
  //  eng oddiy, ishonchli (server'dan eng yangi); kichik ortiqcha so'rov

  USUL 2 — SET QUERY DATA (keshni TO'G'RIDAN yangilash — so'rovsiz, tez):
  onSuccess: (newProduct) => {
    queryClient.setQueryData(["products"], (old) => [...old, newProduct]);   // keshga qo'sh
  }
  //  so'rov yo'q (tezroq), lekin qo'lda (server bilan farq xavfi)

  IERARXIK INVALIDATION (key massivi — 2.6):
  invalidateQueries({ queryKey: ["products"] })   // ["products"], ["products", id], ... HAMMASI

  ┌────────────────────────────────────────────────────────────┐
  │ invalidate: "eskirgan" belgi  qayta yuklash (ishonchli)    │
  │ setQueryData: keshni to'g'ridan (so'rovsiz, tez — ehtiyot)  │
  └────────────────────────────────────────────────────────────┘

   invalidate — eng keng (qayta yuklash — server eng yangi); setQueryData — tez (qo'lda)
   Ierarxik key — umumiy invalidate barcha ostki query'ni bekor qiladi (2.6)

Invalidation va cache update — mutation'dan keyin keshni yangilash (server bilan sinxron tutish). Ikki usul: Usul 1 — invalidateQueries (eng keng, eng ishonchli): queryClient.invalidateQueries({ queryKey: ["products"] }) — bu query'ni (va barcha ostki ["products", ...] query'larini — ierarxik — 2.6) "eskirgan" deb belgilaydi ular qayta yuklanadi (server'dan eng yangi ma'lumot). Oddiy, ishonchli, lekin kichik ortiqcha so'rov (server'ga boradi). Usul 2 — setQueryData (keshni to'g'ridan yangilash — so'rovsiz, tezroq): queryClient.setQueryData(["products"], (old) => [...old, newProduct]) — keshni to'g'ridan o'zgartiradi (so'rov yuborilmaydi — tezroq), lekin qo'lda (server bilan farq xavfi — masalan server qo'shimcha maydon qo'shca, kesh eskirgan bo'ladi). Ierarxik invalidation: invalidateQueries({ queryKey: ["products"] })["products"]dan boshlangan barcha query'ni bekor qiladi (["products", id], ["products", "list", filter] — hammasi — 2.6). Ikki nuqta: (1) invalidate — eng keng (qayta yuklash — server eng yangi, ishonchli); setQueryData — tez (qo'lda kesh yangilash — ehtiyot bilan, optimistic uchun — 2.10); (2) ierarxik key — umumiy invalidate barcha ostki query'ni bekor qiladi (key strukturasining foydasi). Amalda invalidate ko'pincha yetadi (oddiy, ishonchli).

2.10. Optimistic updates

text
  OPTIMISTIC UPDATE — server javobini KUTMASDAN UI'ni darrov yangilash (12.3: 2.9):

  const mutation = useMutation({
    mutationFn: toggleLike,
    onMutate: async (productId) => {                  //  MUTATION'dan OLDIN (darrov):
      await queryClient.cancelQueries({ queryKey: ["products"] });   // joriy so'rovni to'xtat
      const previous = queryClient.getQueryData(["products"]);       // eski'ni sakla (rollback uchun)
      queryClient.setQueryData(["products"], (old) =>                // UI darrov yangila:
        old.map((p) => p.id === productId ? { ...p, liked: !p.liked } : p)
      );
      return { previous };                            // context (rollback uchun)
    },
    onError: (err, productId, context) => {
      queryClient.setQueryData(["products"], context.previous);      //  xato  ROLLBACK
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ["products"] });     // oxirida server bilan sync
    },
  });

  JARAYON: onMutate (darrov UI)  server  onError (rollback) yoki onSettled (sync)

   Optimistic — onMutate (darrov UI) + onError (rollback) + onSettled (sync)
   "Yoqdi", "saqlash" — darrov his (server kutmaydi); xato bo'lsa orqaga

Optimistic updates — server javobini kutmasdan UI'ni darrov yangilash (12.3: 2.9 — TanStack Query versiyasi). useMutationda uch callback bilan: (1) onMutate — mutation'dan oldin (darrov ishlaydi): joriy so'rovlarni to'xtatish (cancelQueries — eski so'rov javobi UI'ni buzmasin), eski ma'lumotni saqlash (getQueryData — rollback uchun), va keshni darrov yangilash (setQueryData — UI o'zgaradi, server javobini kutmasdan); previousni context sifatida qaytaradi. (2) onError — xato bo'lsa rollback (setQueryData(..., context.previous) — eski holatga qaytarish). (3) onSettled — oxirida (muvaffaqiyat yoki xato — baribir) server bilan sinxronlash (invalidateQueries). Jarayon: onMutate (darrov UI) server so'rov onError (rollback) yoki onSettled (sync). Ikki nuqta: (1) optimistic — onMutate (darrov UI) + onError (rollback) + onSettled (sync) — uchchovi birga; (2) "yoqdi", "saqlash", "qo'shish" kabi amallar uchun ideal (foydalanuvchi darrov natijani ko'radi — server kutmaydi; xato bo'lsa jim orqaga qaytadi). Bu — eng silliq, eng tez his beruvchi UX naqshi (zamonaviy ilovalarning belgisi — Instagram "like", Twitter "retweet" kabi).

2.11. Pagination va infinite queries

text
  PAGINATION (sahifalash — keepPreviousData bilan silliq):
  const { data, isFetching } = useQuery({
    queryKey: ["products", page],            // page key'da (o'zgarsa — yangi sahifa)
    queryFn: () => fetchProducts(page),
    placeholderData: keepPreviousData,       //  yangi sahifa yuklanganda eski'ni ushla (sakramaydi)
  });
  //  "Keyingi" bossa — eski sahifa ko'rinib turadi, yangisi fonda (silliq)

  INFINITE QUERIES (cheksiz scroll — "ko'proq yuklash"):
  const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
    queryKey: ["products"],
    queryFn: ({ pageParam }) => fetchProducts(pageParam),
    initialPageParam: 1,
    getNextPageParam: (lastPage) => lastPage.nextCursor,   // keyingi sahifa param'i
  });
  // data.pages — barcha yuklangan sahifalar; fetchNextPage() — keyingisini yukla
  //  cheksiz scroll / "Ko'proq" tugmasi (feed, ro'yxat)

   Pagination — keepPreviousData (sahifa almashganda sakramaydi — silliq)
   useInfiniteQuery — cheksiz scroll (fetchNextPage + getNextPageParam — feed)

Pagination va infinite queries — katta ro'yxatlarni sahifalash va cheksiz scroll. Pagination (sahifalash) — queryKeyda pageni qo'shasiz (["products", page] — page o'zgarsa yangi sahifa), va placeholderData: keepPreviousData — yangi sahifa yuklanayotganda eski sahifani ushlab turadi (UI sakramaydi — "Keyingi" bosganda eski ko'rinib turadi, yangisi fonda yuklanadi — silliq UX, bo'sh ekran yo'q). Infinite queries (cheksiz scroll — "ko'proq yuklash" / feed) — useInfiniteQuery: queryFn: ({ pageParam }) => fetchProducts(pageParam), initialPageParam: 1 (boshlang'ich), getNextPageParam: (lastPage) => lastPage.nextCursor (keyingi sahifa parametri — null bo'lsa oxiri); data.pages (barcha yuklangan sahifalar massivi), fetchNextPage() (keyingisini yuklash), hasNextPage (yana bormi). Bu — cheksiz feed (Instagram, Twitter), "Ko'proq yuklash" tugmasi, yoki scroll bilan avtomatik yuklash (Intersection Observer — 2.17 — bilan) uchun ideal. Ikki nuqta: (1) paginationkeepPreviousData (sahifa almashganda sakramaydi — silliq); (2) useInfiniteQuery — cheksiz scroll (fetchNextPage + getNextPageParam — feed, katta ro'yxat). TanStack Query bu murakkab naqshlarni (qo'lda juda qiyin) bir necha qatorga qisqartiradi — bu uning eng kuchli tomonlaridan biri.

2.12. Prefetching, DevTools va RTK Query bilan yakuniy qaror

text
  PREFETCHING — ma'lumotni OLDINDAN yuklash (foydalanuvchi so'rashidan oldin — 11.8: 2.10):
  // hover'da keyingi sahifa ma'lumotini oldindan yukla (darrov ochilsin):
  const prefetch = () => queryClient.prefetchQuery({ queryKey: ["product", id], queryFn: ... });
  <Link onMouseEnter={prefetch} to={`/products/${id}`}>...</Link>

  REACT QUERY DEVTOOLS (kesh holatini ko'pish — ajoyib):
  <ReactQueryDevtools initialIsOpen={false} />   // har query, kesh, holat — vizual

  RTK QUERY vs TANSTACK QUERY (yakuniy qaror — 2.1):
  ┌──────────────────────────────────────────────────────────┐
  │ Redux loyiha (allaqachon RTK)  RTK Query (tabiiy)        │
  │ Redux'siz / yangi loyiha  TanStack Query (eng keng)      │
  │ Ikkalasi server state'ni zo'r hal qiladi (falsafa bir xil)│
  └──────────────────────────────────────────────────────────┘

   Prefetching — hover'da oldindan (navigatsiya darrov — UX); DevTools — kesh debug
   Yakuniy: server state  har doim Query (RTK yoki TanStack); client  Redux/Zustand/Context

Prefetching, DevTools va yakuniy qaror — TanStack Query'ning qolgan imkoniyatlari va qaror. Prefetching — ma'lumotni foydalanuvchi so'rashidan oldin yuklash (11.8: 2.10 — kechikishni yo'qotish): queryClient.prefetchQuery({ queryKey, queryFn }) — masalan <Link onMouseEnter={prefetch}> (hover'da keyingi sahifa ma'lumotini fonda yukla — bosganda darrov ochilsin). React Query DevTools (<ReactQueryDevtools />) — kesh holatini vizual ko'rsatadi (har query, uning holati — fresh/stale/fetching, kesh ma'lumoti) — debugging uchun ajoyib (RTK Query'ning Redux DevTools'idek). RTK Query vs TanStack Query yakuniy qaror 2.1-bob: Redux loyiha (allaqachon RTK ishlatsa) RTK Query (tabiiy, qo'shimcha kutubxona kerak emas); Redux'siz yoki yangi loyiha TanStack Query (eng keng, moslashuvchan); ikkalasi ham server state'ni zo'r hal qiladi (falsafa bir xil — 12.3: 2.1). Ikki yakuniy nuqta: (1) prefetching — hover'da oldindan yuklash (navigatsiya darrov — UX); DevTools — kesh debug (kuchli vosita); (2) eng muhim qoida — server state har doim Query (RTK yoki TanStack), client state esa Redux/Zustand/Context (12.1: 2.10). Bu ajratishni anglash — zamonaviy frontend arxitekturasining cho'qqisi (server va client state — har xil muammo, har xil vosita). TanStack Query — bugun har React loyihada (Redux bo'lsin yoki yo'q) ishlatiladigan, server bilan ishlashni tubdan soddalashtirgan vosita.

2.13. useQuery opsiyalari — select, refetch, retry, status

text
  useQuery'ning MUHIM QO'SHIMCHA OPSIYALARI:

  select — data'ni TRANSFORMATSIYA qilish (faqat kerakli qismi/formasi):
  const { data } = useQuery({
    queryKey: ["products"],
    queryFn: fetchProducts,
    select: (data) => data.map((p) => p.name),   // faqat nomlar (butun massiv emas)
  });
   select faqat DATA'ni o'zgartiradi (kesh butun holicha saqlanadi)
   select referensiyasi barqaror bo'lsa — keraksiz render yo'q (memoized)

  refetch* — QACHON avtomatik qayta yuklash (default: hammasi true):
  refetchOnWindowFocus: true   — oyna fokusga qaytganda (tab almashib qaytganda)
  refetchOnMount: true         — komponent mount bo'lganda (stale bo'lsa)
  refetchOnReconnect: true     — internet uzilib-ulanganda
  refetchInterval: 5000        — har 5s (polling — real-time'ga yaqin)

  retry — xato bo'lganda qayta urinish (default: 3):
  retry: 3                     — 3 marta (exponential backoff bilan)
  retry: false                 — urinmaslik (masalan 404'da)
  retry: (count, err) => err.status !== 404 && count < 2   // shartli

  status / fetchStatus — ikki alohida holat (v5):
  status:      "pending" | "error" | "success"   (DATA holati)
  fetchStatus: "fetching" | "paused" | "idle"    (SO'ROV holati)
   ikki o'q: ma'lumot bormi (status) + so'rov ketyaptimi (fetchStatus)

   select — data transform (kesh emas); refetch* — qachon yangilash; retry — xatoda
   status (data) + fetchStatus (so'rov) — ikki mustaqil o'q (v5)

useQuery opsiyalariqueryKey/queryFndan tashqari kundalik ishlatiladigan sozlamalar. selectdatani transformatsiya qiladi (faqat kerakli qismini/formasini olish): select: (data) => data.map(p => p.name) — komponent butun massiv o'rniga faqat nomlarni oladi. Muhim: select faqat datani o'zgartiradi (kesh butun holicha saqlanadi — boshqa komponent to'liq ma'lumotni olishi mumkin), va agar select referensiyasi barqaror bo'lsa (komponent tashqarida yoki useCallback), transform faqat ma'lumot o'zgarganda ishlaydi — keraksiz render bo'lmaydi (memoizatsiya). refetchOnWindowFocus (default true) — oyna fokusga qaytganda avtomatik qayta yuklash (foydalanuvchi boshqa tab'dan qaytganda ma'lumot yangilanadi — juda foydali standart); refetchOnMount — komponent mount'da (stale bo'lsa); refetchOnReconnect — internet uzilib qayta ulanganda; refetchInterval: 5000 — har 5 soniyada avtomatik (polling — real-time'ga yaqin, masalan jonli hisoblagich). retry — xato bo'lganda qayta urinish (default: 3 marta, exponential backoff — kutish oralig'i ortib boradi): retry: false (umuman urinmaslik — masalan 404 uchun mantiqsiz), yoki shartli funksiya (retry: (count, err) => err.status !== 404 && count < 2). status va fetchStatus (v5'da ikki alohida o'q): statusma'lumot holati ("pending" | "error" | "success"), fetchStatusso'rov holati ("fetching" | "paused" — internet yo'q | "idle"). Ikki nuqta: (1) select — data transform (kesh emas, memoized); refetch* — qachon avtomatik yangilash (fokus/mount/reconnect/interval); retry — xatoda qayta urinish; (2) status (ma'lumot bormi) + fetchStatus (so'rov ketyaptimi) — ikki mustaqil o'q; isPending/isFetching shulardan hosil bo'ladi.

2.14. Query cancellation — AbortSignal

text
  QUERY CANCELLATION — bekor qilingan so'rovni TO'XTATISH (resurs tejash):

  queryFn AbortSignal oladi (TanStack Query avtomatik beradi):
  const { data } = useQuery({
    queryKey: ["products", search],
    queryFn: ({ signal }) => fetch(`/api/products?q=${search}`, { signal })
      .then((r) => r.json()),                 //  signal — fetch'ga uzatiladi
  });
   key o'zgarsa (search yangi) yoki komponent unmount — ESKI so'rov BEKOR qilinadi
   axios ham qo'llaydi: axios.get(url, { signal })

  cancelQueries — QO'LDA bekor qilish (optimistic'da — 2.10):
  await queryClient.cancelQueries({ queryKey: ["products"] });

   signal'ni queryFn'dan fetch/axios'ga uzat — eski so'rov avtomatik bekor
   Tez o'zgaruvchan key (qidiruv) — eski so'rovlar bekor (race yo'q, tejamli)

Query cancellation — bekor qilingan so'rovni to'xtatish (resurs tejash va race'ni yo'qotish). TanStack Query queryFnga avtomatik AbortSignal beradi: queryFn: ({ signal }) => fetch(url, { signal })signalni fetchga (yoki axiosga — axios.get(url, { signal })) uzatsangiz, query kaliti o'zgarganda (masalan qidiruv matni yangilanganda) yoki komponent unmount bo'lganda eski so'rov avtomatik bekor qilinadi. Bu tez o'zgaruvchan query'lar (jonli qidiruv — har harf bosishda yangi so'rov) uchun juda muhim: eski, endi kerak bo'lmagan so'rovlar bekor bo'ladi — race condition yo'qoladi (11.5'dagi useEffect+fetch'ning eng katta og'rig'i) va tarmoq tejaladi. Qo'lda bekor qilish uchun queryClient.cancelQueries({ queryKey }) ishlatiladi (optimistic update'da onMutateda — eski so'rov javobi optimistic UI'ni buzmasin — 2.10). Ikki nuqta: (1) signalni queryFndan fetch/axiosga uzatish — key o'zgarsa/unmount bo'lsa eski so'rov avtomatik bekor; (2) tez o'zgaruvchan key (qidiruv, filtr) uchun bu race'ni yo'qotadi va tarmoqni tejaydi — deyarli bepul (bir qatorlik { signal }).

2.15. Suspense mode — useSuspenseQuery

text
  SUSPENSE MODE — loading holatini <Suspense> ga topshirish 11.8-bob:

  ODATIY (isPending qo'lda tekshiriladi):
  const { data, isPending } = useQuery({ queryKey: ["user"], queryFn: fetchUser });
  if (isPending) return <Spinner />;

  SUSPENSE (isPending YO'Q — Suspense ushlaydi):
  import { useSuspenseQuery } from "@tanstack/react-query";
  const { data } = useSuspenseQuery({ queryKey: ["user"], queryFn: fetchUser });
  // data — DARROV mavjud (undefined emas — TypeScript ham biladi)
  // loading  eng yaqin <Suspense fallback={<Spinner/>}> ushlaydi
  // error  eng yaqin <ErrorBoundary> ushlaydi

  <ErrorBoundary fallback={<Error/>}>
    <Suspense fallback={<Spinner/>}>
      <UserProfile />               {/* useSuspenseQuery ichida */}
    </Suspense>
  </ErrorBoundary>

   useSuspenseQuery — data doim bor (isPending yo'q); loading/error deklarativ
   Suspense 11.8-bob + ErrorBoundary bilan — toza, deklarativ loading/error

Suspense mode — loading holatini komponent ichida isPending bilan tekshirish o'rniga React'ning <Suspense> mexanizmiga 11.8-bob topshirish. useSuspenseQuery — odatiy useQuerydan farqi: u isPending qaytarmaydidata har doim mavjud (undefined emas — bu TypeScript'da ham foydali: data typi Product bo'ladi, Product | undefined emas — ortiqcha tekshiruvlar yo'q). Yuklanish holatini eng yaqin <Suspense fallback={...}> ushlaydi (fallback ko'rsatiladi), xatoni esa eng yaqin <ErrorBoundary> 11.8-bob ushlaydi. Naqsh: <ErrorBoundary><Suspense fallback={<Spinner/>}><UserProfile/></Suspense></ErrorBoundary>UserProfile ichida useSuspenseQuery ishlaydi, loading/error markazlashgan (deklarativ) boshqariladi. Ikki nuqta: (1) useSuspenseQuerydata doim bor (isPending yo'q), loading <Suspense>, error <ErrorBoundary>ga topshiriladi; (2) bu Suspense-asosli arxitektura 11.8-bob uchun toza yechim — ayniqsa Next.js va React Server Components bilan tabiiy. Ehtiyot: bir nechta useSuspenseQuery bir komponentda ketma-ket (waterfall) bo'lib qolmasligi uchun useSuspenseQueries (parallel) ishlatiladi.

2.16. SSR, hydration va TypeScript

text
  SSR / HYDRATION (Next.js — 13-QISM, server'da yuklab, client'ga uzatish):
  // Server: ma'lumotni oldindan yukla:
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({ queryKey: ["products"], queryFn: fetchProducts });
  // dehydrate  HTML bilan client'ga  <HydrationBoundary state={dehydrate(qc)}>
  //  client "yuklanmoqda" ko'rmaydi (server'da tayyor — SEO + tezlik)

  TYPESCRIPT (queryFn tipi  data tipi avtomatik):
  async function fetchProduct(id: string): Promise<Product> { ... }
  const { data } = useQuery({
    queryKey: ["product", id],
    queryFn: () => fetchProduct(id),          // Promise<Product>
  });
  // data: Product | undefined  (isPending true'da undefined — TS majbur qiladi tekshirishga)

   SSR — server'da prefetch + dehydrate/HydrationBoundary (Next.js — 13-QISM)
   TS — queryFn qaytaruv tipidan data tipi avtomatik (Product | undefined)

SSR, hydration va TypeScript — ilg'or, ammo muhim mavzular. SSR/hydration (Next.js — 13-QISM'da chuqur): ma'lumotni server'da oldindan yuklab (queryClient.prefetchQuery), keshni dehydrate qilib (JSON'ga aylantirib) HTML bilan client'ga uzatish, so'ng client'da <HydrationBoundary state={dehydrate(queryClient)}> bilan tiklash — natijada foydalanuvchi "yuklanmoqda" holatini ko'rmaydi (server'da ma'lumot tayyor keladi — SEO va birinchi ko'rinish tezligi yaxshilanadi). Bu Next.js App Router bilan tabiiy ishlaydi (server component'da prefetch, client component'da useQuery — kesh tayyor). TypeScript — TanStack Query to'liq tip xavfsiz: queryFnning qaytaruv tipidan (Promise<Product>) data tipi avtomatik kelib chiqadi (Product | undefinedisPending true bo'lganda undefined, shuning uchun TS datani ishlatishdan oldin tekshirishga majbur qiladi — bu xavfsizlik). useSuspenseQuery bilan data to'g'ridan Product 2.15-bob. Generic qo'lda ham beriladi (useQuery<Product[]>), lekin queryFnni to'g'ri tiplasangiz — kerak emas. Ikki nuqta: (1) SSR — server'da prefetchQuery + dehydrate/HydrationBoundary (Next.js — 13-QISM cross-ref); (2) TSqueryFn qaytaruv tipidan data tipi avtomatik (Product | undefined — tekshirishga majbur, xavfsiz). Bu ikki xususiyat TanStack Query'ni katta, production ilovalarda ishonchli qiladi.


3. Sintaksis — tez ma'lumotnoma

text
SETUP 2.2-bob:       new QueryClient(); <QueryClientProvider client={qc}><App/></QueryClientProvider>
useQuery 2.3-bob:    useQuery({ queryKey: ["x", id], queryFn: () => fetch(...).then(r=>r.json()) })
HOLAT 2.5-bob:       { data, isPending, isError, error, isFetching, refetch }
KESH 2.4-bob:        staleTime (qancha yangi) | gcTime (ishlatilmagan saqlanishi)
KEY 2.6-bob:         ["products"] | ["products", id] | ["products", { filter }]
ENABLED 2.7-bob:     useQuery({ ..., enabled: !!user?.id })  // shartli/dependent
useMutation 2.8-bob: useMutation({ mutationFn, onSuccess: () => qc.invalidateQueries(...) })
                   mutation.mutate(data) / await mutation.mutateAsync(data)
INVALIDATE 2.9-bob:  queryClient.invalidateQueries({ queryKey: ["products"] })
OPTIMISTIC 2.10-bob: onMutate (setQueryData) + onError (rollback) + onSettled (invalidate)
INFINITE 2.11-bob:   useInfiniteQuery({ queryFn:({pageParam}), getNextPageParam }) + fetchNextPage
SELECT 2.13-bob:     useQuery({ ..., select: (data) => data.map(x => x.name) })  // transform
REFETCH 2.13-bob:    refetchOnWindowFocus | refetchInterval | retry: 3 | staleTime
CANCEL 2.14-bob:     queryFn: ({ signal }) => fetch(url, { signal })   // avtomatik bekor
SUSPENSE 2.15-bob:   useSuspenseQuery({ queryKey, queryFn })  // data doim bor + <Suspense>

4. Batafsil kod namunalari

Misol 1 — Setup (QueryClient + Provider — 2.2)

tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: { staleTime: 60 * 1000, retry: 1, refetchOnWindowFocus: true },
  },
});

function Root() {
  return (
    <QueryClientProvider client={queryClient}>
      <App />
      <ReactQueryDevtools initialIsOpen={false} />   {/* kesh DevTools 2.12-bob */}
    </QueryClientProvider>
  );
}

Misol 2 — useQuery (asosiy — 2.3, 2.5)

tsx
import { useQuery } from "@tanstack/react-query";

async function fetchProducts(): Promise<Product[]> {
  const res = await fetch("/api/products");
  if (!res.ok) throw new Error("Server xatosi");
  return res.json();
}

function Products() {
  const { data, isPending, isError, error, isFetching } = useQuery({
    queryKey: ["products"],
    queryFn: fetchProducts,
  });

  if (isPending) return <Skeleton />;
  if (isError) return <Error message={error.message} />;
  if (!data.length) return <Empty />;

  return (
    <div>
      {isFetching && <small>Yangilanmoqda...</small>}   {/* fonda 2.4-bob */}
      <ul>{data.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
    </div>
  );
}
//  Hech qanday useEffect/useState/loading qo'lda yo'q — TanStack Query AVTOMATIK

Misol 3 — Argument va custom hook (2.3, 2.6)

ts
// Custom hook — query'ni o'rab (qayta ishlatiladigan — 11.7):
export function useProduct(id: string) {
  return useQuery({
    queryKey: ["product", id],              // id key'da (cache key — 2.6)
    queryFn: () => fetch(`/api/products/${id}`).then((r) => r.json()),
    enabled: !!id,                          // id bo'lganda (2.7)
  });
}

// Ishlatish:
function ProductDetail({ id }: { id: string }) {
  const { data: product, isPending } = useProduct(id);
  if (isPending) return <Spinner />;
  return <h1>{product.name}</h1>;
}

Misol 4 — Query key factory (2.6)

ts
// Markazlashgan key'lar (typo va invalidation xatolaridan saqlaydi):
export const productKeys = {
  all: ["products"] as const,
  lists: () => [...productKeys.all, "list"] as const,
  list: (filters: ProductFilters) => [...productKeys.lists(), filters] as const,
  details: () => [...productKeys.all, "detail"] as const,
  detail: (id: string) => [...productKeys.details(), id] as const,
};

// Ishlatish:
useQuery({ queryKey: productKeys.list({ category: "phone" }), queryFn: ... });
useQuery({ queryKey: productKeys.detail("5"), queryFn: ... });
// Invalidation: queryClient.invalidateQueries({ queryKey: productKeys.all });  // hammasi (2.9)

Misol 5 — Dependent query (enabled — 2.7)

tsx
function UserProjects({ email }: { email: string }) {
  // 1. Avval user (email bo'yicha):
  const { data: user } = useQuery({
    queryKey: ["user", email],
    queryFn: () => fetchUserByEmail(email),
  });

  // 2. Keyin user'ning projects'i (user kelguncha kutadi):
  const { data: projects, isPending } = useQuery({
    queryKey: ["projects", user?.id],
    queryFn: () => fetchProjects(user!.id),
    enabled: !!user?.id,                    //  user bo'lganda ishlaydi (2.7)
  });

  if (!user) return <p>Foydalanuvchi yuklanmoqda...</p>;
  if (isPending) return <p>Loyihalar yuklanmoqda...</p>;
  return <ul>{projects?.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}

Misol 6 — Parallel queries (2.7)

tsx
function Dashboard() {
  const users = useQuery({ queryKey: ["users"], queryFn: fetchUsers });
  const orders = useQuery({ queryKey: ["orders"], queryFn: fetchOrders });
  const stats = useQuery({ queryKey: ["stats"], queryFn: fetchStats });
  //  uchchovi PARALLEL yuklanadi (bir-birini kutmaydi — tez — 2.7)

  if (users.isPending || orders.isPending || stats.isPending) return <Skeleton />;
  return <div>{/* users.data, orders.data, stats.data */}</div>;
}

Misol 7 — useMutation va invalidation (2.8, 2.9)

tsx
import { useMutation, useQueryClient } from "@tanstack/react-query";

function AddProduct() {
  const queryClient = useQueryClient();
  const mutation = useMutation({
    mutationFn: (newProduct: Partial<Product>) =>
      fetch("/api/products", { method: "POST", body: JSON.stringify(newProduct) }).then((r) => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["products"] });   // ro'yxat qayta yuklanadi (2.9)
      toast.success("Qo'shildi");
    },
    onError: (error) => toast.error(error.message),
  });

  return (
    <button onClick={() => mutation.mutate({ name: "Yangi" })} disabled={mutation.isPending}>
      {mutation.isPending ? "Yuborilmoqda..." : "+ Qo'shish"}
    </button>
  );
}

Misol 8 — Optimistic update (2.10)

tsx
function useLikeProduct() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => fetch(`/api/products/${id}/like`, { method: "POST" }),
    onMutate: async (id) => {
      await queryClient.cancelQueries({ queryKey: ["products"] });          // so'rovni to'xtat
      const previous = queryClient.getQueryData<Product[]>(["products"]);   // eski'ni sakla
      queryClient.setQueryData<Product[]>(["products"], (old) =>            // UI darrov:
        old?.map((p) => (p.id === id ? { ...p, liked: !p.liked } : p))
      );
      return { previous };
    },
    onError: (err, id, context) => {
      queryClient.setQueryData(["products"], context?.previous);            // rollback (2.10)
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ["products"] });            // sync
    },
  });
}
//  "Yoqdi" darrov  (server kutmasdan); xato bo'lsa orqaga (2.10)

Misol 9 — Pagination (keepPreviousData — 2.11)

tsx
import { keepPreviousData } from "@tanstack/react-query";

function ProductsPaged() {
  const [page, setPage] = useState(1);
  const { data, isFetching, isPlaceholderData } = useQuery({
    queryKey: ["products", page],
    queryFn: () => fetchProducts(page),
    placeholderData: keepPreviousData,        // eski sahifa ushlanadi (sakramaydi — 2.11)
  });

  return (
    <div>
      <ul style={{ opacity: isFetching ? 0.6 : 1 }}>
        {data?.items.map((p: Product) => <li key={p.id}>{p.name}</li>)}
      </ul>
      <button onClick={() => setPage((p) => p - 1)} disabled={page === 1}>Oldingi</button>
      <button onClick={() => setPage((p) => p + 1)} disabled={!data?.hasMore || isPlaceholderData}>
        Keyingi
      </button>
    </div>
  );
}

Misol 10 — Infinite query (cheksiz scroll — 2.11)

tsx
import { useInfiniteQuery } from "@tanstack/react-query";

function Feed() {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ["feed"],
    queryFn: ({ pageParam }) => fetchFeed(pageParam),
    initialPageParam: 1,
    getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,   // keyingi yoki oxiri (2.11)
  });

  return (
    <div>
      {data?.pages.map((page) =>
        page.items.map((post: Post) => <article key={post.id}>{post.title}</article>)
      )}
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? "Yuklanmoqda..." : "Ko'proq"}
        </button>
      )}
    </div>
  );
}
//  data.pages — barcha sahifalar; fetchNextPage — keyingisi (cheksiz scroll — 2.11)

Misol 11 — Prefetching (hover'da — 2.12)

tsx
function ProductCard({ product }: { product: Product }) {
  const queryClient = useQueryClient();

  const prefetch = () => {
    queryClient.prefetchQuery({
      queryKey: ["product", product.id],
      queryFn: () => fetch(`/api/products/${product.id}`).then((r) => r.json()),
    });
  };

  return (
    <Link to={`/products/${product.id}`} onMouseEnter={prefetch}>   {/* hover'da oldindan */}
      {product.name}
    </Link>
  );
}
//  Hover'da ma'lumot fonda yuklanadi  bosganda darrov ochiladi (kechikishsiz — 2.12)

Misol 12 — setQueryData (to'g'ridan kesh — 2.9)

tsx
// Mutation natijasini darrov keshga qo'shish (so'rovsiz — tezroq):
const mutation = useMutation({
  mutationFn: addProduct,
  onSuccess: (newProduct) => {
    // Invalidate o'rniga — keshga to'g'ridan qo'sh (so'rov yo'q):
    queryClient.setQueryData<Product[]>(["products"], (old) => [...(old ?? []), newProduct]);
  },
});
//  setQueryData — server javobini darrov keshga (qayta so'rovsiz — 2.9). Ehtiyot: server bilan mos bo'lsin

Misol 13 — Custom hooks (API qatlami — 11.7)

ts
// features/products/queries.ts — barcha product query/mutation custom hook (tartibli):
export function useProducts(filters?: ProductFilters) {
  return useQuery({ queryKey: productKeys.list(filters ?? {}), queryFn: () => fetchProducts(filters) });
}
export function useProduct(id: string) {
  return useQuery({ queryKey: productKeys.detail(id), queryFn: () => fetchProduct(id), enabled: !!id });
}
export function useAddProduct() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: addProduct,
    onSuccess: () => qc.invalidateQueries({ queryKey: productKeys.all }),
  });
}
// Komponentda: const { data } = useProducts();  — toza, qayta ishlatiladigan (11.7)

Misol 14 — RTK Query vs TanStack qaror (2.12)

ts
// SERVER STATE — har doim Query (RTK yoki TanStack):

// Redux loyiha (allaqachon RTK ishlatadi)  RTK Query 12.3-bob:
//   createApi + endpoints (Redux store'da kesh)

// Redux'siz / yangi loyiha  TanStack Query (bu bob):
//   useQuery/useMutation (mustaqil, eng keng)

// CLIENT STATE — Query EMAS:
//   theme/auth/modal  Context 12.1-bob yoki Zustand (12.5)
//   katta murakkab  Redux (12.2)

//  Yakuniy: server  Query (RTK/TanStack); client  Context/Zustand/Redux (12.1: 2.10)
//    Zamonaviy tendensiya: TanStack Query (server) + Zustand (kichik client) — yengil, kuchli

Misol 15 — select (data transform — 2.13)

tsx
// select — keshdagi butun ma'lumotdan faqat kerakli qismini olish (transform):
function ProductNames() {
  const { data: names } = useQuery({
    queryKey: ["products"],
    queryFn: fetchProducts,
    select: (data) => data.map((p) => p.name),   // faqat nomlar (2.13)
  });
  return <ul>{names?.map((n) => <li key={n}>{n}</li>)}</ul>;
}
//  Kesh butun Product[] holicha saqlanadi; select faqat SHU komponent data'sini o'zgartiradi.
//    Boshqa komponent select'siz to'liq ma'lumotni oladi — bitta so'rov, ko'p ko'rinish.

Misol 16 — Qidiruv: AbortSignal + keepPreviousData (2.13, 2.14)

tsx
function ProductSearch() {
  const [query, setQuery] = useState("");
  const { data, isFetching } = useQuery({
    queryKey: ["products", "search", query],
    queryFn: ({ signal }) =>                       //  signal — avtomatik bekor (2.14)
      fetch(`/api/products?q=${query}`, { signal }).then((r) => r.json()),
    enabled: query.length > 1,                     // 2+ harfda qidir (2.7)
    placeholderData: keepPreviousData,             // eski natija ushlanadi (2.11)
  });

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Qidirish..." />
      {isFetching && <small>Qidirilmoqda...</small>}
      <ul>{data?.map((p: Product) => <li key={p.id}>{p.name}</li>)}</ul>
    </div>
  );
}
//  Har harfda query key o'zgaradi  eski so'rov signal bilan BEKOR (race yo'q — 2.14)

Misol 17 — Suspense mode (useSuspenseQuery — 2.15)

tsx
import { useSuspenseQuery } from "@tanstack/react-query";
import { ErrorBoundary } from "react-error-boundary";
import { Suspense } from "react";

function UserProfile({ id }: { id: string }) {
  const { data: user } = useSuspenseQuery({    //  isPending YO'Q — data doim bor (2.15)
    queryKey: ["user", id],
    queryFn: () => fetchUser(id),
  });
  return <h1>{user.name}</h1>;                  // user: User (undefined emas — TS ham biladi)
}

function Page({ id }: { id: string }) {
  return (
    <ErrorBoundary fallback={<p>Xatolik yuz berdi</p>}>
      <Suspense fallback={<Spinner />}>          {/* loading shu yerda 11.8-bob */}
        <UserProfile id={id} />
      </Suspense>
    </ErrorBoundary>
  );
}
//  loading  <Suspense>, error  <ErrorBoundary> (deklarativ — komponent ichida tekshiruv yo'q)

Misol 18 — refetchInterval polling (real-time'ga yaqin — 2.13)

tsx
// Jonli ma'lumot (buyurtma holati, hisoblagich) — muntazam avtomatik yangilash:
function OrderStatus({ orderId }: { orderId: string }) {
  const { data } = useQuery({
    queryKey: ["order", orderId],
    queryFn: () => fetchOrder(orderId),
    refetchInterval: 5000,          // har 5s avtomatik qayta yuklash (polling — 2.13)
    refetchIntervalInBackground: false,   // tab fondaligida to'xtaydi (tejamli)
  });
  return <span>Holat: {data?.status}</span>;
}
//  refetchInterval — WebSocket bo'lmaganda "real-time'ga yaqin" arzon yechim (2.13)

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

1) Server state

text
 useEffect + fetch + useState (loading/error/race qo'lda — 11.5)
 useQuery (hammasi avtomatik — 2.3)

2) Query key

text
 queryKey: "products" (string — noto'g'ri)
 queryKey: ["products", id] (massiv — 2.6)

3) Dependent query

text
 user kelmasdan projects so'rash (xato — user undefined)
 enabled: !!user?.id (user bo'lganda — 2.7)

4) Kesh yangilash

text
 mutation'dan keyin qo'lda refetch yoki sahifani yangilash
 invalidateQueries (onSuccess — 2.9)

5) Pagination

text
 sahifa almashganda oq ekran (placeholderData yo'q)
 keepPreviousData (eski ushlanadi — silliq — 2.11)

6) Client vs server

text
 theme/modal'ni useQuery'da (server emas)
 client state  Context/Zustand; server  Query (2.12)

7) Qidiruvda race

text
 har harfda fetch (signal yo'q — eski javob kechikib UI'ni buzadi — 11.5)
 queryFn: ({ signal }) => fetch(url, { signal }) (eski bekor — 2.14)

8) Ma'lumotdan qismini olish

text
 komponentda data'ni har render qayta hisoblash/filtrlash (keraksiz render)
 select: (data) => ... (memoized transform — 2.13)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — No QueryClient set

Sababi: QueryClientProvider o'ralmagan 2.2-bob. Yechimi: <QueryClientProvider client={qc}> ildizda (Misol 1).

Xato 2 — Query har render qayta ishlaydi

Sababi: queryKey/queryFn har render yangi (inline obyekt key — 2.6). Yechimi: barqaror key (primitiv/memoized); queryFn referensiyasi muhim emas (TanStack key bilan kuzatadi).

Xato 3 — Mutation'dan keyin ro'yxat eskirgan

Sababi: invalidateQueries yo'q 2.9-bob. Yechimi: onSuccess: () => qc.invalidateQueries(...) (Misol 7).

Xato 4 — Dependent query undefined xatosi

Sababi: enabled yo'q, user kelmasdan so'rov 2.7-bob. Yechimi: enabled: !!user?.id (Misol 5).

Xato 5 — Optimistic rollback ishlamaydi

Sababi: onMutate'da previous saqlanmagan yoki onError'da rollback yo'q 2.10-bob. Yechimi: onMutate (getQueryData) + onError (setQueryData previous — Misol 8).

Xato 6 — Pagination'da sahifa "sakraydi"

Sababi: keepPreviousData yo'q 2.11-bob. Yechimi: placeholderData: keepPreviousData (Misol 9).

Xato 7 — Juda ko'p so'rov (refetch tez-tez)

Sababi: staleTime: 0 (default — har mount/focus refetch — 2.4). Yechimi: staleTime oshir (kam o'zgaradigan ma'lumotga — Misol 1).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • RTK Query 12.3-bob: muqobil server-state (Redux ekotizimida); bir xil falsafa.
  • Context 12.1-bob: server state — Context'da emas, Query'da (12.1: 2.10).
  • Custom hooks 11.7-bob: query/mutation custom hook (useProducts — Misol 13).
  • Forma 11.10-bob: mutation + RHF (submit mutate).
  • Routing 11.9-bob: prefetch (hover); loader o'rniga Query (11.9: 2.13).
  • Loading holatlari (11.15: 2.8): isPending/isFetching — 4 holat avtomatik.
  • Suspense 11.8-bob: useSuspenseQuery (Suspense + ErrorBoundary bilan — 2.15, 11.8: 2.12).
  • Qidiruv/race 11.5-bob: queryFn AbortSignal — eski so'rov avtomatik bekor 2.14-bob.
  • Next.js 13.5-bob: server-side prefetch + dehydrate/HydrationBoundary (TanStack Query SSR — 2.16).
  • TypeScript 11.3-bob: queryFn tipidan data tipi avtomatik 2.16-bob.

8. Eng yaxshi amaliyotlar (best practices)

  • Server state TanStack Query'da (useEffect+fetch emas — 2.3).
  • Query key massiv + factory (struktura, tartibli — 2.6, Misol 4).
  • Custom hooklar (useProducts/useAddProduct — qayta ishlatiladigan — Misol 13).
  • invalidateQueries onSuccess (kesh sync — 2.9).
  • staleTime to'g'ri (kam o'zgaradiganga oshir — kam so'rov — 2.4).
  • enabled dependent query'ga (oldingi natija kelguncha — 2.7).
  • keepPreviousData pagination'ga (silliq — 2.11).
  • Optimistic tez UX'ga (onMutate + rollback — 2.10).
  • DevTools'dan foydalan (kesh debug — 2.12).
  • select bilan transform (kerakli qismi — keraksiz render kam — 2.13).
  • AbortSignal qidiruvga (eski so'rov bekor — race yo'q — 2.14).
  • Server Query; client Context/Zustand/Redux (2.12, 12.1: 2.10).

9. Amaliy loyiha: "TanStack Query bilan To'liq Data Qatlami"

TanStack Query'ni real ilovada — query, mutation, infinite, optimistic — mustahkamlash.

Maqsad

Mahsulot/feed ilovasi uchun to'liq TanStack Query data qatlami: ro'yxat, tafsilot, CRUD, infinite scroll, optimistic.

Talablar (requirements)

  1. Setup: QueryClient + Provider + DevTools (Misol 1).
  2. useQuery: ro'yxat va tafsilot (custom hook — Misol 2, 3, 13).
  3. Query key factory: markazlashgan key'lar (Misol 4, 2.6).
  4. Dependent query: biri ikkinchisiga bog'liq (enabled — Misol 5, 2.7).
  5. useMutation: add/update/delete + invalidation (Misol 7, 2.8, 2.9).
  6. Optimistic: like/toggle (onMutate + rollback — Misol 8, 2.10).
  7. Pagination yoki infinite: keepPreviousData yoki useInfiniteQuery (Misol 9, 10).
  8. Prefetch: hover'da tafsilot (Misol 11, 2.12).
  9. staleTime: kam o'zgaradigan ma'lumotga to'g'ri 2.4-bob.
  10. Holat: isPending/isFetching/error to'g'ri (4 holat — 2.5).

Maslahatlar (hint)

  • Query'larni custom hook'ga o'ra (useProducts — qayta ishlatiladigan — Misol 13).
  • Key — massiv + factory (typo/invalidation xatolaridan — Misol 4).
  • Mutation'dan keyin invalidateQueries (qo'lda refetch emas — Misol 7).
  • Dependent — enabled (Xato 4).
  • Infinite — getNextPageParam (Misol 10).
  • DevTools bilan kesh holatini kuzat (debug — 2.12).
  • Client state (theme/modal) — Query'da emas (Zustand/Context — 2.12).

"Tayyor" mezonlari (acceptance criteria)

  • Setup (QueryClient + Provider + DevTools).
  • useQuery (ro'yxat/tafsilot — custom hook).
  • Query key factory.
  • Dependent query (enabled).
  • CRUD mutation + invalidation.
  • Optimistic update (rollback bilan).
  • Pagination yoki infinite scroll.
  • Prefetch (hover).
  • staleTime to'g'ri (kam so'rov).
  • Holatlar (isPending/isFetching/error).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda TanStack Query'ni chuqur o'rgandik:

  • TanStack Query (mustaqil server-state — 2.1); setup (QueryClient — 2.2); useQuery (queryKey/queryFn — 2.3); hayot sikli/kesh (staleTime/gcTime — 2.4); holatlar 2.5-bob.
  • Query keys (factory — 2.6); dependent/parallel 2.7-bob; useMutation 2.8-bob; invalidation/cache update 2.9-bob; optimistic 2.10-bob.
  • Pagination/infinite 2.11-bob; prefetching/DevTools/qaror 2.12-bob.
  • Opsiyalar (select/refetch/retry/status — 2.13); cancellation (AbortSignal — 2.14); Suspense mode (useSuspenseQuery — 2.15); SSR/hydration va TypeScript 2.16-bob.

Endi siz server ma'lumotini TanStack Query bilan professional — kesh, invalidation, optimistic, infinite scroll, prefetch — boshqara olasiz, va useEffect+fetch'ning butun og'rig'idan qutulasiz. Eng muhimi — server state (Query) vs client state (Context/Zustand/Redux) ajratishini to'liq tushunasiz.

Keyingi bob — 12.5-bob: Zustand. Redux katta ilova uchun zo'r, lekin u biroz boilerplate va sozlama talab qiladi. Zustand — kichik, yengil, oddiy client-state kutubxonasi (Redux'ning minimalist muqobili): bir necha qator bilan global store yaratasiz, hech qanday Provider, boilerplate, yoki murakkab sozlama yo'q — lekin tabiiy selektor (Context cheklovini hal qiladi), middleware, va persist bilan. Zamonaviy tendensiya — TanStack Query (server) + Zustand (kichik client) — yengil, kuchli, kam kod. Bu — ko'p loyiha uchun ideal kombinatsiya.


Foydalanilgan rasmiy/ishonchli manbalar

  • TanStack Query rasmiy hujjatiuseQuery, useMutation, query keys, caching (staleTime/gcTime), invalidation, optimistic updates, infinite queries, select, refetch opsiyalari, retry.
  • TanStack Query — Guides — "Query Cancellation" (AbortSignal), "Dependent Queries" (enabled), "Paginated / Lagged Queries" (placeholderData: keepPreviousData), "Suspense" (useSuspenseQuery).
  • TkDodo blog (Dominik Dorfmeister — TanStack Query maintainer) — "Practical React Query", "React Query as a State Manager", "Effective React Query Keys", "React Query and TypeScript".
  • TanStack Query — SSR & Server ComponentsprefetchQuery, dehydrate, HydrationBoundary (Next.js integratsiyasi — 13-QISM).
  • TanStack Query v5 migration guideisLoading isPending, keepPreviousData placeholderData, cacheTime gcTime, status/fetchStatus ajratish.
  • React rasmiy hujjati<Suspense> va Error Boundary (Suspense mode konteksti — 11.8).

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
12.4-bob: TanStack Query (React Query) — Wisar