🛑 1. The Problem First: "แอปเทพ แต่ส่งลิ้งก์ให้เพื่อนไม่ได้"
ลองนึกถึงวันที่คุณสร้างระบบ Search และ Filter สินค้าที่ซับซ้อนมาก:
// ❌ Naive Approach: เก็บ Filter ทุกอย่างไว้ใน Zustand Store
const { filters, setFilters } = useStore();
// 🌋 พัง! พอลูกค้าเจอสินค้าราคาถูกใจแล้วส่งลิ้งก์ให้เพื่อนใน LINE
# เพื่อนเปิดมากลับเจอ 'หน้าว่าง' หรือสินค้าคนละตัว เพราะข้อมูล Filter
# ถูกขังอยู่แค่ในคอมพิวเตอร์ของคุณเท่านั้น นี่คือการทำลาย User Experience พื้นฐานครับ
ปัญหา: การเก็บ State ไว้ในหน่วยความจำ (RAM) ของเครื่องลูกข่ายคือการทำลาย "ความเป็นอินเทอร์เน็ต" (Hyperlinkability) ข้อมูลที่สำคัญต่อหน้าจอนั้นๆ ควรจะ "ลอยตัว" อยู่ที่ไหนสักแห่งที่ทุุกคนเข้าถึงได้เหมือนกันครับ
💡 2. Real-Life Analogy: ป้ายบอกทางในห้างสรรพสินค้า
- Global Store (Redux/Zustand): เหมือน "ความทรงจำส่วนตัว". คุณจำได้ว่าคุณกำลังเดินไปร้านรองเท้าชั้น 3 แต่เพื่อนที่คุณโทรหาไม่รู้เรื่องด้วยเลย จนกว่าคุณจะพูดบอกเขาทุุกอย่างซ้ำอีกรอบ
- URL Search Params: เหมือน "เลขที่ล็อกแจ้งบนชั้นวาง". ไม่ว่าคุณจะเดินไปมุมไหนของห้าง ถ้าคุณบอกว่า "เจอกันที่ล๊อก B2 ชั้น 3" ทุุกคนจะหาคุณเจอทันทีเพราะข้อมูลมันติดอยู่กับ 'สถานที่' (URL) นั้นแล้ว
- Server State (SWR/React Query): เหมือน "พนักงานสอบถามข้อมูล". คุณไม่ต้องท่องจำทุุกอย่างเอง แค่ถามพนักงานเมื่อต้องการรู้ และพนักงานจะจำไว้ให้ (Cache) เผื่อมีคนอื่นมาถามซ้ำในเวลาใกล้ๆ กัน
🚀 3. Execution Journey: มหากาพย์การวางข้อมูลให้ถูกที่
Senior จะไม่เริ่มต้นที่การเลือก Library แต่จะเริ่มต้นที่การเลือก "ที่อยู่" ให้ข้อมูล
🛠 Step-by-step:
- The URL First Rule: ทุุกอย่างที่ส่งผลต่อการ Filter, Search, หรือ Pagination ต้องอยู่ใน
searchParamsเสมอ - The Server State Split: แยกข้อมูลที่มาจาก API ออกจาก Logic ของ UI และส่งมอบหน้าที่การจัดการ (Auto-refetch, Caching) ให้ SWR หรือ React Query
- The Local Sync: ใช้
useStateเฉพาะงานที่เป็นชั่วคราวมากๆ เช่น การพิมพ์ใน Form หรือการเปิด/ปิด Modal เล็กๆ - The Global Guard: ใช้ Global Store อย่าง Zustand หรือ Redux เฉพาะงานที่ข้ามหน้าและไม่เกี่ยวกับ URL จริงๆ (เช่น เครื่องเล่นเพลงที่เล่นต่อเนื่อง)
// ✅ Best Practice: การใช้ URL เป็น State สำหรับการ Filter
"use client";
import { useRouter, useSearchParams } from "next/navigation";
function ColorFilter() {
const router = useRouter();
const searchParams = useSearchParams();
const handleSelect = (color) => {
const params = new URLSearchParams(searchParams);
params.set("color", color);
router.push(`?${params.toString()}`); // 🛠 State เปลี่ยนผ่าน URL ทันที
};
// ...
}
🪤 4. The Junior Trap: โรค "Zustand Everything" (สาดทุุกอย่างใส่ Store)
จูเนียร์มักจะเอาข้อมูลจาก API มาเก็บไว้ใน Global Store:
// ❌ Junior Trap: พยายามจัดการ Cache ด้วยตัวเอง
const { products, setProducts } = useStore();
useEffect(() => {
fetch('/api/products').then(res => setProducts(res.data));
}, []);
// 🌋 พัง! คุณต้องมานั่งจัดการเองว่าหน้าอื่นต้องดึงใหม่ไหม? พอหน้าอื่นดึง ข้อมูลจะทับกันไหม?
# และถ้าเน็ตดับล่ะจะทำยังไง? ทุุกอย่างกลายเป็นงานที่ซับซ้อนเกินจำเป็น
ระวัง: การทำ Caching เป็นเรื่องยาก อย่าพยายามเป็นฮีโร่เขียนเอง ✅ การแก้ไข: จงใช้ Server State Libraries (SWR/React Query) เพื่อให้เขารับผิดชอบเรื่องความซับซ้อนนี้แทนคุณครับ
⚖️ 5. The Why Matrix: URL vs Global Store vs Server State
| หัวข้อ | URL (Params) | Global Store (RAM) | Server State (Cache) |
|---|---|---|---|
| ความทนทาน | 🚀 สูงสุด (Refresh ไม่หาย) | ต่ำ (หายเมื่อปิดหน้า) | ปานกลาง (พึ่งพา Cache) |
| การแชร์ | 🚀 ทำได้ทันที (Copy Link) | ทำไม่ได้ | ทำไม่ได้ |
| เหมาะกับ | Filter, Search, Pagination | UI Settings, Auth Data | ข้อมูลจาก API / Database |
| สรุป | Senior เลือกใช้อย่างแรก | ใช้เมื่อจำเป็นจริงๆ | ทุุกโปรเจกต์ต้องมี |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "หัวใจของ State Management คือการทำอย่างไรให้ข้อมูล 'นิ่ง' และ 'แชร์ได้' ง่ายที่สุด". เลิกสรรหา Library ที่ซับซ้อนมาแก้ปัญหาที่ URL แก้ได้เองอยู่แล้ว แล้วแอปของคุณจะเบาและใช้งานง่ายขึ้นทั่งในมุมของ Dev และ User ครับ!