[React Series] Tanstack Query: Data Fetching ที่เปลี่ยนทุกอย่าง
[React Series] Tanstack Query: Data Fetching ที่เปลี่ยนทุกอย่าง
สวัสดีครับทุกคน! วันนี้เราจะมาพูดถึง Tanstack Query (เดิมชื่อ React Query) ซึ่งเป็น Library ที่เปลี่ยนวิธีการจัดการ Data Fetching ใน React ไปอย่างสิ้นเชิง
ทำไมต้อง Tanstack Query?
ลองนึกภาพว่าคุณต้องดึงข้อมูลจาก API ใน React App ของคุณ สิ่งที่คุณต้องทำในแบบดั้งเดิมมีอะไรบ้าง:
- สร้าง State สำหรับเก็บข้อมูล
- สร้าง State สำหรับ Loading
- สร้าง State สำหรับ Error
- เรียก API ใน useEffect
- จัดการ Cleanup เมื่อ Component Unmount
- จัดการ Caching เอง
- จัดการ Optimistic Updates
- จัดการ Retry เมื่อ Request ล้มเหลว
ยุ่งยากใช่ไหมครับ? Tanstack Query มาช่วยแก้ปัญหาทั้งหมดนี้!
เริ่มต้นใช้งาน
1. ติดตั้ง
npm install @tanstack/react-query
# หรือ
yarn add @tanstack/react-query
2. Setup Provider
ก่อนใช้งาน ต้องห่อ App ด้วย QueryClientProvider ก่อน:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
);
}
3. ใช้งาน useQuery
มาดูตัวอย่างการดึงข้อมูล Users จาก API กัน:
import { useQuery } from '@tanstack/react-query';
function UsersList() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
},
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
เห็นไหมครับ? แค่นี้เราก็ได้ Data Fetching ที่มี Caching, Loading State, และ Error Handling มาให้แล้ว!
Advanced Features
1. Automatic Caching
Tanstack Query มี Cache ให้อัตโนมัติ:
// Component 1: ดึงข้อมูล
const { data } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
});
// Component 2: อยู่คนละหน้า แต่ใช้ queryKey เดียวกัน
// จะได้ข้อมูลจาก Cache ทันที ไม่ต้องเรียก API ใหม่!
const { data } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
});
2. Stale Time และ Cache Time
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // ข้อมูลจะเป็น "stale" หลังจาก 5 นาที
gcTime: 10 * 60 * 1000, // ข้อมูลจะถูกลบออกจาก cache หลังจาก 10 นาที
refetchOnMount: true, // refetch ทุกครั้งที่ component mount
refetchOnWindowFocus: true,// refetch ทุกครั้งที่ window focus
});
3. Manual Refetch
const { data, refetch } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
// เรียก refetch เมื่อต้องการ
<button onClick={() => refetch()}>Refresh</button>
4. useMutation สำหรับการแก้ไขข้อมูล
สำหรับการ POST, PUT, DELETE ใช้ useMutation:
import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateUser() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newUser) => {
return fetch('https://api.example.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser),
});
},
// เมื่อสร้าง user สำเร็จ จะ refetch 'users' query
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
const handleSubmit = (userData) => {
mutation.mutate(userData);
};
return (
<div>
{mutation.isPending && 'Creating...'}
{mutation.isError && `Error: ${mutation.error.message}`}
{mutation.isSuccess && 'User created!'}
<button onClick={() => handleSubmit({ name: 'John' })}>
Create User
</button>
</div>
);
}
5. Optimistic Updates
ทำให้ UI อัพเดททันทีก่อนที่ Server จะตอบกลับ:
const mutation = useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({ queryKey: ['users', newUser.id] });
// Snapshot the previous value
const previousUser = queryClient.getQueryData(['users', newUser.id]);
// Optimistically update to the new value
queryClient.setQueryData(['users', newUser.id], newUser);
// Return context with the snapshotted value
return { previousUser };
},
// If the mutation fails, use the context to roll back
onError: (err, newUser, context) => {
queryClient.setQueryData(
['users', newUser.id],
context.previousUser
);
},
// Always refetch after error or success
onSettled: (user) => {
queryClient.invalidateQueries({ queryKey: ['users', user.id] });
},
});
6. Parallel Queries
เรียกหลาย queries พร้อมกัน:
import { useQueries } from '@tanstack/react-query';
function Dashboard() {
const results = useQueries({
queries: [
{ queryKey: ['users'], queryFn: fetchUsers },
{ queryKey: ['posts'], queryFn: fetchPosts },
{ queryKey: ['notifications'], queryFn: fetchNotifications },
],
});
// results[0].data = users
// results[1].data = posts
// results[2].data = notifications
}
7. Dependent Queries
Query ที่ต้องรอ query ก่อนหน้า:
// ดึง user ก่อน
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
// ดึง posts หลังจากได้ user แล้ว (เมื่อ userId มีค่า)
const { data: posts } = useQuery({
queryKey: ['posts', userId],
queryFn: () => fetchPosts(userId),
enabled: !!userId, // จะไม่ทำงานจนกว่า userId จะมีค่า
});
สรุป
Tanstack Query เป็นเครื่องมือที่ทรงพลังมากสำหรับการจัดการ Data Fetching ใน React ช่วยให้เรา:
- ลดโค้ด - จากเดิมต้องเขียน state, effect, cleanup เอง ตอนนี้แค่ใช้ useQuery
- มี Caching อัตโนมัติ - ลดการเรียก API ซ้ำ
- จัดการ Loading/Error - ง่ายๆ ผ่าน isLoading, isError
- Optimistic Updates - UI ตอบสนองเร็ว
- Automatic Retry - ลองใหม่เมื่อ network มีปัญหา
ถ้าคุณยังใช้ useEffect + fetch แบบเดิม ลองหันมาลอง Tanstack Query ดูนะครับ รับรองว่าจะเปลี่ยนวิธีคิดในการทำ Data Fetching ไปตลอด!
ในบทความหน้า เราจะมาดู Advanced Topics เช่น Infinite Queries, React Query DevTools, และการทำ Testing กันครับ
หากมีคำถามหรือต้องการ discuss กันเพิ่มเติม ฝาก comment ไว้ได้เลยครับ! 🚀