🛑 1. The Problem First: "โค้ดที่เต็มไปด้วย if-loading และ data-stale"
ลองนึกถึงวันที่คุณต้องดึงข้อมูลรายการสินค้ามาโชว์:
// ❌ Naive Approach: เขียน Fetch แบบดิบๆ (เหนื่อยทุุก API)
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setIsLoading(false));
}, []);
// 🌋 พัง! คุณต้องเขียน boilerplate นี้ซ้ำๆ 100 รอบในทุุกหน้า
# และที่แย่กว่านั้นคือถ้า User สลับ Tab ไปมา ข้อมูลในหน้าอาจจะ 'บูด' (Stale)
# ไปแล้วโดยที่คุณไม่มีระบบดึงใหม่ให้อัตโนมัติเลยครับ
ปัญหา: การดึงข้อมูลไม่ใช่แค่เรื่องของการยิง Request แต่คือเรื่องของการจัดการ State ถังกลาง (Cache), การรับมือกับ Network ที่ไม่นิ่ง และการทำให้ข้อมูล "สดใหม่" (Fresh) อยู่เสมอโดยไม่รบกวน User ครับ
💡 2. Real-Life Analogy: ห้องสมุดอัจฉริยะ
- Standard Fetch (useEffect): เหมือนคุณเดินไปที่เคาน์เตอร์บรรณารักษ์ทุุกครั้งเพื่อถามหาหนังสือเล่มเดิม บรรณารักษ์ต้องเดินไปหยิบจากชั้นหลังสุดทุุกรอบ (เสียเวลา)
- React Query (Caching): เหมือน "โต๊ะพักหนังสือ". ถ้ามีคนเคยขอดูหนังสือเล่มนี้เมื่อ 1 นาทีที่แล้ว บรรณารักษ์จะหยิบจากโต๊ะข้างหน้าให้ทันที (เร็วมาก) และถ้าหนังสือเล่มนั้นถูกแก้ไข (Mutation) บรรณารักษ์จะรีบเอาไปเปลี่ยนบนชั้นพ้นที (Invalidation)
- Stale-While-Revalidate: เหมือนพนักงานบอกคุณว่า "นี่คือข้อมูลล่าสุดที่เรามีนะดูไปก่อน เดี๋ยวผมวิ่งไปเช็คที่คลังอีกทีว่ามีการอัปเดตไหม ถ้ามีเดี๋ยวผมแก้บนหน้ากระดาษให้ทันที"
🚀 3. Execution Journey: มหากาพย์การใช้เครื่องทุ่นแรง
Senior จะไม่เขียนระบบ Fetching เอง แต่จะใช้ระบบที่ "คิดมาเผื่อทุุกสภาวะ" แล้ว
🛠 Step-by-step:
- The Query Key: กำหนด 'ชื่อ' ให้กับข้อมูลทุุกชุด เพื่อให้ React Query รู้ว่าต้องดึงจาก Cache ตัวไหน
- The Fetcher Wrapper: สร้างฟังก์ชันดึงข้อมูลที่เป็นอิสระ (Pure Function) เพื่อให้ง่ายต่อการทดสอบ (Testing)
- The Simple Hook: เรียกใช้
useQueryพร้อมจัดการสถานะisLoadingและerrorในที่เดียว - The Smart Invalidator: ใช้
mutation.onSuccessเพื่อสั่งให้ข้อมูลหน้าอื่นอัปเดตตามทันทีเมื่อเราแก้ไขอะไรบางอย่าง
// ✅ Best Practice: การใช้ React Query ขั้นพื้นฐาน (แต่ทรงพลัง)
const { data, isLoading } = useQuery({
queryKey: ["products"], // 🛠 ชื่อของ Cache
queryFn: () => axios.get("/api/products").then((res) => res.data),
staleTime: 1000 * 60 * 5, // 🛠 ให้ข้อมูลสดเป็นเวลา 5 นาที ไม่ต้องยิงใหม่ซ้ำๆ
});
🪤 4. The Junior Trap: โรค "Manual Synchronizing"
จูเนียร์มักจะพยายามอัปเดต State เองหลังจากที่ POST ข้อมูลสำเร็จ:
// ❌ Junior Trap: แก้ไข State ในเครื่องแบบเดาๆ
const handleUpdate = async () => {
await updateProduct(id, newData);
// 🌋 พัง! จูเนียร์จะเขียน:
setProducts(products.map(p => p.id === id ? newData : p));
# การทำแบบนี้เสี่ยงมากเพราะ 'ความจริง' ในฐานข้อมูลอาจจะเปลี่ยนไปแล้ว
# หรือมี Logic หลังบ้านที่เราไม่ได้ทำเลียนแบบไว้ใน Client
ระวัง: อย่าพยายามเป็นฐานข้อมูลจำลองใน Client
✅ การแก้ไข: จงใช้ queryClient.invalidateQueries เพื่อสั่งให้ React Query ไปดึง 'ความจริง' มาจาก Server ใหม่ทุุกครั้งหลังการแก้ไขครับ
⚖️ 5. The Why Matrix: Redux (Server State) vs React Query
| หัวข้อ | Redux (แบบเก่า) | React Query (Senior) |
|---|---|---|
| Boilerplate | 🌋 มหาศาล (Action/Reducer) | ⚡⚡ น้อยมาก (1 Hook) |
| Caching | ต้องเขียนเองทุุกบรรทัด | 🚀 ทำมาให้ฟรีและฉลาดมาก |
| Auto-update | ไม่มี | 🚀 รันอัตโนมัติเมื่อ Window Focus |
| ความรู้สึกของ Dev | เหนื่อยและบั๊กเยอะ | 😍 สนุกและระบบนิ่งกว่าเยอะ |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "เราควรเอาเวลาไปแก้ปัญหาทางธุรกิจ ไม่ใช่เอาเวลามานั่งเขียน if-loading ซ้ำๆ 100 รอบ". React Query ไม่ใช่แค่ Library แต่มันคือการเปลี่ยน 'ภาระ' ให้กลายเป็น 'ระบบ' ที่จะทำให้แอปของคุณนิ่งและเร็วขึ้นอย่างเห็นได้ชัดครับ!