TanStack Query — Next.js bilan ishlash, server va client farqi
Muammodan boshlaylik
Siz Client Component da ma'lumot olmoqchisiz. Oddiy useEffect + fetch yozdingiz:
'use client'
export function ProductList() {
const [products, setProducts] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
fetch('/api/products')
.then(r => r.json())
.then(setProducts)
.catch(setError)
.finally(() => setLoading(false))
}, [])
if (loading) return <Spinner />
if (error) return <p>Xato</p>
return <ul>{products.map(...)}</ul>
}
Ishlaydi. Lekin muammolar bor:
- Foydalanuvchi boshqa sahifaga o'tib qaytsa — ma'lumot qayta yuklanadi
- Bir sahifada bir xil ma'lumot kerak bo'lsa — ikki marta so'rov ketadi
- Xato bo'lganda qayta urinish yo'q
- Ma'lumot eskirganini bilish yo'q
TanStack Query (avval React Query) bu muammolarni hal qiladi.
O'rnatish
npm install @tanstack/react-query
QueryClientProvider — butun ilova uchun bir marta o'rnatiladi:
// app/providers.tsx
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useState } from 'react'
export function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 daqiqa — ma'lumot "yangi" hisoblanadi
},
},
}))
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}
// app/layout.tsx
import { Providers } from './providers'
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}
useQuery — ma'lumot olish
'use client'
import { useQuery } from '@tanstack/react-query'
export function ProductList() {
const { data, isLoading, error } = useQuery({
queryKey: ['products'],
queryFn: () => fetch('/api/products').then(r => r.json()),
})
if (isLoading) return <Spinner />
if (error) return <p>Xato: {error.message}</p>
return (
<ul>
{data.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
)
}
queryKey — kesh kaliti. ['products'] — bu so'rovning nomi. Bir xil queryKey bo'lsa — keshdan qaytariladi, qayta so'rov ketmaydi.
queryFn — ma'lumot oluvchi funksiya. Promise qaytarishi kerak.
queryKey — kesh kaliti
queryKey — TanStack Query ning eng muhim tushunchasi. U kesh kaliti sifatida ishlaydi.
// Har xil key — har xil kesh
useQuery({ queryKey: ['products'], ... }) // barcha mahsulotlar
useQuery({ queryKey: ['products', id], ... }) // bitta mahsulot
useQuery({ queryKey: ['products', { page: 2 }], ... }) // 2-sahifa
Bir xil queryKey bo'lsa — bir xil kesh. Sahifada 3 ta komponent ['products'] so'rasa — bitta so'rov ketadi.
useMutation — ma'lumot o'zgartirish
'use client'
import { useMutation, useQueryClient } from '@tanstack/react-query'
export function AddToCart({ productId }: { productId: number }) {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: (productId: number) =>
fetch('/api/cart', {
method: 'POST',
body: JSON.stringify({ productId }),
}).then(r => r.json()),
onSuccess: () => {
// Savat ma'lumotini yangilash
queryClient.invalidateQueries({ queryKey: ['cart'] })
},
})
return (
<button
onClick={() => mutation.mutate(productId)}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Qo\'shilmoqda...' : 'Savatga qo\'shish'}
</button>
)
}
invalidateQueries — ['cart'] keshini "eskirgan" deb belgilaydi. Keyingi render da qayta yuklanadi.
Server Component bilan birga ishlatish
TanStack Query — Client Component uchun. Server Component da kerak emas — u to'g'ridan-to'g'ri async/await ishlatadi.
Lekin ikkalasini birga ishlatish mumkin — prefetching orqali. Server Component ma'lumotni oldindan yuklab, Client Component ga uzatadi:
// app/products/page.tsx — Server Component
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
import { ProductList } from './ProductList'
export default async function ProductsPage() {
const queryClient = new QueryClient()
// Server da ma'lumotni oldindan yuklash
await queryClient.prefetchQuery({
queryKey: ['products'],
queryFn: () => fetch('https://api.example.com/products').then(r => r.json()),
})
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ProductList />
</HydrationBoundary>
)
}
// ProductList.tsx — Client Component
'use client'
import { useQuery } from '@tanstack/react-query'
export function ProductList() {
const { data } = useQuery({
queryKey: ['products'],
queryFn: () => fetch('/api/products').then(r => r.json()),
})
// data — serverda yuklangan, darhol ko'rinadi. Loading yo'q!
return <ul>{data?.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
Foydalanuvchi sahifani ochganda — ma'lumot allaqachon bor. Loading spinner ko'rinmaydi. Keyin foydalanuvchi o'zgartirishlar qilsa — Client da yangilanadi.
staleTime va gcTime
useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
staleTime: 5 * 60 * 1000, // 5 daqiqa — ma'lumot "yangi"
gcTime: 10 * 60 * 1000, // 10 daqiqa — keshdan o'chiriladi
})
staleTime — ma'lumot qancha vaqt "yangi" hisoblanadi. Bu vaqt ichida qayta so'rov ketmaydi.
gcTime — ma'lumot keshda qancha vaqt saqlanadi. Komponent unmount bo'lgandan keyin.
Tez-tez uchraydigan xatolar
1. QueryClientProvider yo'q
Error: No QueryClient set, use QueryClientProvider to set one
Providers ni layout.tsx ga qo'shishni unutish — eng ko'p uchraydigan xato.
2. QueryClient ni komponent ichida yaratish
// ❌ — har render da yangi QueryClient, kesh yo'qoladi
'use client'
export function Providers({ children }) {
const queryClient = new QueryClient() // xato!
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
// ✅ — useState bilan bir marta
const [queryClient] = useState(() => new QueryClient())
3. Server Component da useQuery ishlatish
// ❌ — Server Component da hook ishlamaydi
export default async function Page() {
const { data } = useQuery(...) // xato!
}
// ✅ — Server Component da to'g'ridan-to'g'ri fetch
export default async function Page() {
const data = await fetch('/api/data').then(r => r.json())
}
Xulosa
TanStack Query — Client Component da ma'lumot olish uchun. Kesh, qayta urinish, deduplikatsiya — avtomatik.
useQuery → ma'lumot olish, kesh, loading/error holatlari
useMutation → ma'lumot o'zgartirish (POST, PUT, DELETE)
queryKey → kesh kaliti, bir xil key = bir xil kesh
invalidateQueries → keshni yangilash
prefetchQuery → Server Component da oldindan yuklash
Server Component da TanStack Query kerak emas — async/await yetarli. Client Component da interaktiv ma'lumot uchun ishlatiladi.
Keyingi dars
22-dars: Server Action — nima, qanday ishlaydi, form bilan ishlatish
Forma yuborishda API endpoint yozmasdan, to'g'ridan-to'g'ri server funksiyasini chaqirish.