TanStack Query үндэс
Өмнөх хичээлд fetch + useEffect-ээр өгөгдөл татсан — гэхдээ loading, error, race condition, cache гэх мэт олон зүйлийг гараар зохицуулах хэрэгтэй болсон. TanStack Query (хуучнаар React Query) эдгээрийг бүгдийг автоматаар зохицуулдаг. Гурван мөр кодоор мэргэжлийн data fetching хийнэ!
Суулгах ба тохиргоо
npm install @tanstack/react-query
Аппын дээд түвшинд QueryClientProvider нэмнэ:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// QueryClient нь cache болон глобал тохиргоог агуулдаг
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 минут cache-ийг "шинэ" гэж үзнэ
retry: 2, // Алдаа гарвал 2 удаа дахин оролдоно
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
);
}
useQuery — өгөгдөл татах
useQuery нь fetch + useEffect + useState гурвыг нэгэн дор орлуулна:
import { useQuery } from "@tanstack/react-query";
// Fetch функц — зөвхөн өгөгдөл татах логик, state байхгүй
async function fetchUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) throw new Error("Хэрэглэгчид татаж чадсангүй");
return res.json();
}
function UserList() {
const {
data: users,
isLoading,
isError,
error,
} = useQuery({
queryKey: ["users"], // Cache-н түлхүүр — өвөрмөц байх ёстой
queryFn: fetchUsers, // Өгөгдөл татах функц
});
if (isLoading) return <p>Ачааллаж байна...</p>;
if (isError) return <p style={{ color: "red" }}>Алдаа: {error.message}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} — {user.email}
</li>
))}
</ul>
);
}
queryKey: ["users"] нь cache-н нэр юм — энэ key-тэй өгөгдөл cache-д байвал сервер рүү дахин хандалгүй шууд буцаана. Хэрэглэгч хуудас солиод буцаж ирэхэд шууд харагдана, дараа background-д шинэчлэгдэнэ.
Dynamic query — params-аас хамаарах
import { useQuery } from "@tanstack/react-query";
import { useParams } from "react-router-dom";
async function fetchPost(postId) {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`,
);
if (!res.ok) throw new Error("Нийтлэл олдсонгүй");
return res.json();
}
function PostDetail() {
const { postId } = useParams();
const {
data: post,
isLoading,
isError,
} = useQuery({
// postId өөрчлөгдөхөд автоматаар дахин татна
queryKey: ["posts", postId],
queryFn: () => fetchPost(postId),
enabled: !!postId, // postId байхад л татна
});
if (isLoading) return <p>Нийтлэл ачааллаж байна...</p>;
if (isError) return <p style={{ color: "red" }}>Нийтлэл олдсонгүй.</p>;
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
);
}
queryKey-д postId нэмсний ачаар өөр нийтлэл рүү очиход автоматаар шинэ хүсэлт явуулдаг. Cache-д байвал хуучнаараа харуулж background-д шинэчилнэ.
useMutation — өгөгдөл илгээх
Өгөгдөл үүсгэх, засах, устгахад useMutation ашиглана:
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
async function createPost(newPost) {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newPost),
});
if (!res.ok) throw new Error("Нийтлэл үүсгэж чадсангүй");
return res.json();
}
function CreatePost() {
const [title, setTitle] = useState("");
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
// Амжилттай болсны дараа нийтлэлийн жагсаалтыг шинэчилнэ
queryClient.invalidateQueries({ queryKey: ["posts"] });
setTitle("");
},
onError: (err) => {
console.error("Алдаа:", err.message);
},
});
function handleSubmit(e) {
e.preventDefault();
mutation.mutate({ title, userId: 1 });
}
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Нийтлэлийн гарчиг"
disabled={mutation.isPending}
/>
<button type="submit" disabled={mutation.isPending || !title}>
{mutation.isPending ? "Илгээж байна..." : "Нийтлэх"}
</button>
{mutation.isSuccess && (
<p style={{ color: "green" }}>Амжилттай нийтлэгдлээ!</p>
)}
{mutation.isError && (
<p style={{ color: "red" }}>{mutation.error.message}</p>
)}
</form>
);
}
queryClient.invalidateQueries нь тухайн key-тэй cache-г "хуучирсан" гэж тэмдэглэдэг — тэр даруй дахин татаж шинэ өгөгдлийг харуулна.
fetch + useEffect vs TanStack Query
Хоёрыг харьцуулбал ялгаа тодорхой:
// ❌ fetch + useEffect — 30+ мөр, гараар бичих их
function OldWay() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
fetch("/api/data")
.then((r) => r.json())
.then((d) => {
if (!cancelled) setData(d);
})
.catch((e) => {
if (!cancelled) setError(e);
})
.finally(() => {
if (!cancelled) setLoading(false);
});
return () => {
cancelled = true;
};
}, []);
// ...
}
// ✅ TanStack Query — 5 мөр, cache, retry, refetch бүгд автомат
function NewWay() {
const { data, isLoading, isError } = useQuery({
queryKey: ["data"],
queryFn: () => fetch("/api/data").then((r) => r.json()),
});
// ...
}
TanStack Query нь мөн window focus refetch (хэрэглэгч табаас буцаж ирэхэд автоматаар шинэчлэх), background refetch, pagination, infinite scroll зэрэг дэвшилтэт боломжуудтай.
Дараагийн хичээлд:
React Hook Form сурна — маягт (form) зохицуулах хамгийн алдартай library. Validation, error message, submit зохицуулах зэргийг хэд дахин хялбар болгодог.