🛑 1. The Problem First: "โค้ดที่รันได้... แต่ยิง API พรุน"
ลองนึกถึงแอปที่มี Component เล็กๆ 5 ตัวต้องการข้อมูล User โปรไฟล์เหมือนกัน:
// ❌ Naive Approach: ยิงใครยิงมัน (Request Overload)
useEffect(() => {
fetch('/api/user').then(res => res.json()).then(setUser);
}, []); // 🌋 พัง! ถ้ามี 5 Component แปะอยู่ในหน้าเดียว แถบ Network
# จะขึ้นมา 5 Request ทันทีที่โหลดหน้าเว็บ ทั้งที่เป็นข้อมูลตัวเดียวกัน
# แถมยังเสี่ยงต่อ Race Conditions (ข้อมูลเก่าทับข้อมูลใหม่) อีกด้วย
ปัญหา: ประสิทธิภาพไม่ได้เสียไปกับการดึงข้อมูลอย่างเดียว แต่เสียไปกับการ "จัดการข้อมูลซ้ำซ้อน" (Deduplication) และการขาดระบบ Cache ที่ดี ทำให้ความเร็วของเว็บคุณต้องพึ่งพาความเร็วของอินเทอร์เน็ตทุุกวินาทีครับ
💡 2. Real-Life Analogy: ร้านกาแฟที่มีพนักงานเสิร์ฟ 5 คน
- useEffect Fetching: เหมือนลูกค้า 5 คนสั่งกาแฟแบบเดียวกัน แต่พนักงานเสิร์ฟ 5 คนเดินไปสั่งบาริสต้า (Server) 5 รอบพร้อมกัน บาริสต้าต้องทำกาแฟ 5 แก้วทั้งที่จริงๆ บดทีเดียวแล้วแบ่งก็ได้ (เสียทรัพยากร)
- SWR (Stale-While-Revalidate): เหมือน "กระติกกาแฟที่เตรียมไว้แล้ว". เมื่อลูกค้าสั่ง พนักงานหยิบส่งให้ทันที (หน้าเว็บขึ้นไว) แล้วพนักงานค่อยแอบเดินไปเติมกาแฟใหม่ (Background Fetch) เพื่อให้แก้วถัดไปสดใหม่เสมอ
- LocalStorage Versioning: เหมือน "การเขียนวันที่หมดอายุบนกล่องอาหาร". ถ้าคุณเปลี่ยนสูตรอาหาร (Data Schema) แต่ยังกินของเก่าในกล่องเดิม (Old Data) คุณจะท้องเสีย (App Crash) การมี Versioning คือการทิ้งกล่องเก่าเมื่อสูตรเปลี่ยนนั่นเอง
🚀 3. Execution Journey: มหากาพย์การสร้างระบบ Fetching ระดับท็อป
Senior จะมองหาความลื่นไหล (Fluidity) และความทนทาน (Resilience)
🛠 Step-by-step:
- The Deduplicator: ใช้ SWR หรือ React Query เพื่อรวม Request ที่เหมือนกันให้เหลือเพียงหนึ่งเดียว
- The Subscriber: ใช้
useSWRSubscriptionเพื่อแชร์ Event Listener ตัวเดียวข้ามทุุกคอมโพเนนต์ (ประหยัดหน่วยความจำ) - The Secure Storage: ใส่ Versioning และ
try-catchทุุกครั้งที่ยุ่งกับ LocalStorage เพื่อรับมือกับข้อมูลที่อาจเสียได้ - The Smooth Scroll: ใช้
{ passive: true }เมื่อต้องดักฟังก์ชัน Scroll เพื่อให้ Browser ไม่ต้องรอ JS ทำงานเสร็จก่อนจะเลื่อนหน้าจอ
// ✅ Best Practice: ดึงข้อมูลแบบมีชั้นเชิงด้วย SWR
import useSWR from "swr";
function UserProfile() {
// 🛠 Key เดียวกัน = Request เดียวกัน (Deduplication)
const { data, error } = useSWR("/api/user", fetcher);
if (!data) return <Skeleton />; // 🛠 แสดงสถานะกำลังโหลดแบบเนียนๆ
return <div>Welcome, {data.name}</div>;
}
🪤 4. The Junior Trap: โรค "Data Stale Forever"
จูเนียร์มักจะดึงข้อมูลมาเก็บไว้ใน Global State (เช่น Redux/Context) แล้วลืมอัปเดต:
// ❌ Junior Trap: เก็บข้อมูลไว้ใน State จนบูด
const [user, setUser] = useState(cachedUser);
// 🌋 พัง! ข้อมูล User ในหน้าจออาจจะเป็นข้อมูลของเมื่อ 10 นาทีก่อน
# โดยที่ไม่มีกลไกในการ Refresh ใหม่จนกว่า User จะกด Reload หน้าเว็บเอง
# ทำให้เกิดความสับสนเมื่อ User แก้ไขรหัสผ่านแล้วแต่หน้าจอยังขึ้นแบบเดิม
ระวัง: ข้อมูลที่ 'สงบนิ่ง' เกินไป คือข้อมูลที่ทำลายความถูกต้องของระบบ ✅ การแก้ไข: จงใช้ Libraries ที่มีระบบ Auto-Revalidation (เช่น โฟกัสหน้าต่างแล้วดึงข้อมูลใหม่) เพื่อให้ข้อมูลสดใหม่อยู่เสมอครับ
⚖️ 5. The Why Matrix: useEffect vs SWR vs React Query
| หัวข้อ | useEffect (แบบเก่า) | SWR (Vercel) | React Query (Enterprise) |
|---|---|---|---|
| Caching | ไม่มี (ต้องเขียนเอง) | ⚡⚡ อัตโนมัติ | 🚀 ขั้นสูง (Fine-grained) |
| ความง่าย | 🐢 ยาก (Bug เยอะ) | 🚀 ง่ายที่สุด | ปานกลาง (มี Boilerplate) |
| Data Synchronization | ไม่ทำ | ⚡⚡ ทำระดับหนึ่ง | 🚀 ดีที่สุด (DevTools ครบ) |
| เหมาะกับ | โปรเจกต์จิ๋วๆ | Modern Web / Next.js | App ขนาดใหญ่ที่มี State ซับซ้อน |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "การดึงข้อมูลไม่ใช่แค่การเอา JSON มาโชว์ แต่มันคือการบริหาร 'ความหวัง' ของผู้ใช้". เว็บที่ขึ้นรูป Skeleton ทันทีและข้อมูลค่อยๆ โผล่ออกมาอย่างนิ่งสงบ คืองานศิลปะของวิศวกรซอฟต์แวร์ที่ใส่ใจในรายละเอียดครับ!