ในโลกของการพัฒนาเว็บสมัยใหม่ การจัดการสถานะข้อมูลฝั่งไคลเอ็นต์ (client-side data) ที่ซับซ้อนและมีการเรียกใช้ API บ่อยครั้งเป็นความท้าทายที่นักพัฒนาต้องเผชิญ **TanStack React Query** (เดิมคือ React Query) ได้เข้ามาเปลี่ยนแปลงวิธีการจัดการข้อมูลเหล่านี้อย่างสิ้นเชิง ด้วย hooks ที่ทรงพลัง โดยเฉพาะอย่างยิ่ง **`useQuery`** ที่ช่วยให้เราสามารถจัดการ fetching, caching, synchronizing, และ updating server state ได้อย่างง่ายดายและมีประสิทธิภาพ
บทความนี้จะแนะนำให้คุณรู้จักกับ `useQuery` และแสดงให้เห็นว่ามันสามารถทำให้โค้ดของคุณสะอาดขึ้น มีประสิทธิภาพมากขึ้น และประสบการณ์ผู้ใช้ดีขึ้นได้อย่างไร ----- ### ทำไมต้องใช้ TanStack React Query? ก่อนที่เราจะเจาะลึกถึง `useQuery` เรามาดูกันว่าทำไม React Query ถึงเป็นที่นิยมและมีความสำคัญ: * **Caching อัตโนมัติ:** จัดการแคชข้อมูลที่ดึงมาแล้วโดยอัตโนมัติ ลดการเรียก API ซ้ำซ้อน * **Background Refetching:** อัปเดตข้อมูลในเบื้องหลังเพื่อให้ข้อมูลเป็นปัจจุบันอยู่เสมอ โดยไม่บล็อก UI * **Optimistic Updates:** ทำให้ UI ตอบสนองได้รวดเร็วขึ้นโดยการอัปเดตข้อมูลทันที และย้อนกลับหากเกิดข้อผิดพลาด * **Devtools:** มีเครื่องมือ Devtools ที่ยอดเยี่ยมสำหรับการตรวจสอบสถานะของ Query และ Cache * **จัดการสถานะได้ง่ายขึ้น:** ลด boilerplate code ที่เกี่ยวข้องกับการ fetching data (loading, error, success states) * **ประสิทธิภาพที่ดีขึ้น:** ช่วยให้แอปพลิเคชันของคุณทำงานได้เร็วและราบรื่นขึ้น ----- ### การเริ่มต้นใช้งาน ก่อนอื่น คุณต้องติดตั้ง TanStack React Query ในโปรเจกต์ของคุณ: ```bash npm install @tanstack/react-query # หรือ yarn add @tanstack/react-query ``` จากนั้น คุณต้องห่อหุ้มแอปพลิเคชันของคุณด้วย **`QueryClientProvider`** เพื่อให้ `useQuery` สามารถเข้าถึง QueryClient ได้: ```jsx // src/main.jsx หรือ src/index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.jsx'; import './index.css'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; // ไม่บังคับ const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById('root')).render(, ); ``` ----- ### ทำความเข้าใจกับ `useQuery` `useQuery` เป็น React Hook ที่ใช้สำหรับ "fetching" ข้อมูลจากเซิร์ฟเวอร์ มันจะส่งคืนสถานะของ Query (loading, error, success) พร้อมกับข้อมูลที่ได้มา **โครงสร้างพื้นฐาน:** ```javascript const { data, isLoading, isError, error, isSuccess, status } = useQuery({ queryKey: ['uniqueKey'], queryFn: async () => { // ฟังก์ชันสำหรับ fetch ข้อมูล const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }, // options เพิ่มเติม (เช่น staleTime, cacheTime, retry, enabled ฯลฯ) }); ``` **พารามิเตอร์ที่สำคัญ:** 1. **`queryKey` (Array):** * นี่คือ "**คีย์**" ที่ไม่ซ้ำกันสำหรับ Query ของคุณ * React Query ใช้ `queryKey` เพื่อจัดการ Caching, Refetching และ Shared State * ควรเป็น Array เสมอ หากข้อมูลที่คุณดึงมาขึ้นอยู่กับพารามิเตอร์ใดๆ ให้รวมพารามิเตอร์เหล่านั้นไว้ใน `queryKey` ด้วย ตัวอย่าง: * `['todos']` * `['todo', todoId]` * `['posts', { type: 'published', authorId: 123 }]` 2. **`queryFn` (Function):** * ฟังก์ชัน `async` ที่รับผิดชอบในการ fetch ข้อมูล * จะต้องคืนค่าเป็น Promise ที่ Resolve ด้วยข้อมูล หรือ Reject ด้วย Error **ค่าที่ส่งคืนจาก `useQuery`:** * **`data`:** ข้อมูลที่ดึงมาได้เมื่อ Query สำเร็จ (จะเป็น `undefined` ในขณะที่ `isLoading` หรือ `isError`) * **`isLoading` / `isPending`:** (ใน v5 เปลี่ยนเป็น **`isPending`** เพื่อให้ความหมายชัดเจนขึ้นว่าอยู่ในสถานะรอ) Boolean ที่ระบุว่า Query กำลังอยู่ในระหว่างการ fetch ข้อมูลครั้งแรก * **`isError`:** Boolean ที่ระบุว่า Query มีข้อผิดพลาดเกิดขึ้น * **`error`:** อ็อบเจกต์ Error หาก `isError` เป็น `true` * **`isSuccess`:** Boolean ที่ระบุว่า Query สำเร็จและมีข้อมูลพร้อมใช้งาน * **`status`:** สถานะปัจจุบันของ Query (`'pending'`, `'error'`, `'success'`) ----- ### ตัวอย่างการใช้งานจริง มาดูตัวอย่างการดึงข้อมูลรายชื่อโพสต์จาก API ง่ายๆ: ```jsx // src/components/PostsList.jsx import React from 'react'; import { useQuery } from '@tanstack/react-query'; async function fetchPosts() { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); if (!response.ok) { throw new Error('Failed to fetch posts'); } return response.json(); } function PostsList() { const { data: posts, isLoading, isError, error } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, }); if (isLoading) { return {/* แสดง Devtools */} Loading posts...; } if (isError) { returnError: {error.message}; } return (); } export default PostsList; ``` **คำอธิบาย:** 1. เราสร้างฟังก์ชัน `fetchPosts` แยกออกมาเพื่อทำหน้าที่เรียก API 2. เรียกใช้ `useQuery` โดยมี **`queryKey`** เป็น `['posts']` และ **`queryFn`** เป็น `fetchPosts` 3. ตรวจสอบสถานะ **`isLoading`**, **`isError`** และแสดง UI ที่เหมาะสม 4. เมื่อข้อมูลพร้อมใช้งาน (`isSuccess` เป็น `true`) เราก็สามารถเข้าถึง `posts` และนำไปแสดงผลได้เลย ----- ### การจัดการพารามิเตอร์ใน `queryKey` บ่อยครั้งที่เราต้องการดึงข้อมูลโดยอิงจาก ID หรือพารามิเตอร์อื่นๆ `queryKey` สามารถจัดการสิ่งนี้ได้อย่างง่ายดาย: ```jsx // src/components/PostDetail.jsx import React from 'react'; import { useQuery } from '@tanstack/react-query'; async function fetchPostById(postId) { const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`); if (!response.ok) { throw new Error(`Failed to fetch post with ID: ${postId}`); } return response.json(); } function PostDetail({ postId }) { const { data: post, isLoading, isError, error } = useQuery({ queryKey: ['post', postId], // คีย์จะเปลี่ยนไปตาม postId queryFn: () => fetchPostById(postId), // ส่ง postId เข้าไปในฟังก์ชัน fetch enabled: !!postId, // จะเรียก Query ก็ต่อเมื่อ postId มีค่า (ไม่เป็น null/undefined) }); if (isLoading) { returnPosts
{posts.map((post) => (
- ))}
{post.title}
{post.body}
Loading post...; } if (isError) { returnError: {error.message}; } if (!post) { // กรณีที่ postId ไม่มีค่าตั้งแต่แรก returnPlease select a post.} return (); } export default PostDetail; ``` **คำอธิบายเพิ่มเติม:** * **`queryKey: ['post', postId]`:** เมื่อ `postId` เปลี่ยนไป React Query จะรู้ว่านี่คือ Query ใหม่และจะทำการ fetch ข้อมูลใหม่ * **`queryFn: () => fetchPostById(postId)`:** เราใช้ Arrow Function เพื่อส่ง `postId` ไปยัง `fetchPostById` * **`enabled: !!postId`:** นี่คือ Option ที่สำคัญมาก\! มันบอกให้ `useQuery` จะทำการเรียก `queryFn` ก็ต่อเมื่อ `postId` มีค่าเป็น truthy เท่านั้น (ไม่เป็น `null` หรือ `undefined`) ซึ่งมีประโยชน์มากเมื่อเราอาจจะยังไม่มี `postId` ในตอนเริ่มต้น ----- ### Options ที่มีประโยชน์อื่นๆ `useQuery` มี Options มากมายให้เราปรับแต่งพฤติกรรมได้: * **`staleTime` (number | Infinity):** * กำหนดระยะเวลา (มิลลิวินาที) ที่ข้อมูลจะถือว่า "สด" (fresh) * ภายใน `staleTime`, React Query จะไม่ทำการ fetch ข้อมูลใหม่ * ค่าเริ่มต้นคือ `0` (ข้อมูลจะถือว่า stale ทันทีหลังจาก fetch ครั้งแรก) * ถ้าตั้งค่าเป็น `Infinity` ข้อมูลจะไม่มีวัน stale และจะไม่มีการ refetch โดยอัตโนมัติ * **`cacheTime` (number | Infinity):** * กำหนดระยะเวลา (มิลลิวินาที) ที่ข้อมูลจะถูกเก็บไว้ใน cache หลังจากที่ Query ไม่ถูกใช้งานแล้ว * เมื่อ Query ไม่ถูกใช้งานและ `cacheTime` หมดลง ข้อมูลจะถูก garbage-collected * ค่าเริ่มต้นคือ `5 * 60 * 1000` (5 นาที) * **`refetchOnWindowFocus` (boolean | 'always'):** * กำหนดว่าจะให้ Query ทำการ refetch เมื่อหน้าต่างเบราว์เซอร์กลับมาโฟกัสหรือไม่ * ค่าเริ่มต้นคือ `true` * **`retry` (boolean | number):** * กำหนดว่าจะให้ Query ทำการ retry เมื่อเกิดข้อผิดพลาดในการ fetch หรือไม่ * ค่าเริ่มต้นคือ `3` ครั้ง * ตั้งค่าเป็น `false` เพื่อปิดการ retry * **`onSuccess`, `onError`, `onSettled` (Function):** * Callback functions ที่จะถูกเรียกเมื่อ Query สำเร็จ, มีข้อผิดพลาด หรือไม่ว่าจะสำเร็จหรือมีข้อผิดพลาดก็ตาม * **`select` (Function):** * ใช้สำหรับแปลงหรือเลือกข้อมูลบางส่วนจาก `data` ที่ได้มา ก่อนที่จะส่งคืนให้ Component ```javascript const { data: postTitles } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, select: (posts) => posts.map(post => post.title), // เลือกแค่ title }); // postTitles จะเป็น array ของ strings ``` ----- ### สรุป `useQuery` ของ TanStack React Query เป็นเครื่องมือที่ทรงพลังและขาดไม่ได้สำหรับการจัดการข้อมูลในแอปพลิเคชัน React สมัยใหม่ มันช่วยลดความซับซ้อนของการจัดการสถานะการโหลด, ข้อผิดพลาด, และการแคชข้อมูล ทำให้คุณสามารถโฟกัสกับการสร้างฟีเจอร์ได้มากขึ้น การทำความเข้าใจ `queryKey` และ `queryFn` รวมถึง Options ต่างๆ จะช่วยให้คุณสามารถใช้ประโยชน์จาก React Query ได้อย่างเต็มที่ และสร้างแอปพลิเคชันที่ตอบสนองได้ดีและมีประสิทธิภาพ ลองนำ `useQuery` ไปใช้ในโปรเจกต์ถัดไปของคุณ แล้วคุณจะเห็นถึงความแตกต่างอย่างแน่นอน\!{post.title}
{post.body}
0 ความคิดเห็น