🛑 1. The Problem First: เว็บ "กระตุก" เพราะ Render เกินจำเป็น
จูเนียร์ส่วนใหญ่มักจะกลัวการคำนวณสดๆ เลยใช้วิธีเก็บทุกอย่างลง State:
// ❌ Naive Approach: ใช้ useEffect เพื่อ Sync State (ทำให้เกิด Double Render)
function Form() {
const [firstName, setFirst] = useState("John");
const [lastName, setLast] = useState("Doe");
const [fullName, setFull] = useState("");
useEffect(() => {
setFull(firstName + " " + lastName); // 🌋 พัง! พอมัน setFull ปุ๊บ มันจะ Render รอบที่ 2 ทันที
}, [firstName, lastName]);
return <div>{fullName}</div>;
}
ปัญหา: ทุกครั้งที่พิมพ์ชื่อ React จะวาดรูป 2 ครั้ง (ครั้งแรกเปลี่ยนชื่อ ครั้งสองเปลี่ยนชื่อเต็ม) ถ้า Component ใหญ่กว่านี้ เว็บคุณจะเริ่ม "พิมพ์แล้วหน่วง" และเปลือง CPU โดยใช่เหตุครับ
💡 2. Real-Life Analogy: เลขากับการ "จดงาน" ที่ซ้ำซ้อน
ลองนึกภาพว่าคุณเป็น "เจ้านาย" และมี "เลขา (React)".
- Redundant State: คุณสั่งเลขาว่า "จดชื่อจริงไว้ (firstName)" และ "จดนามสกุลไว้ (lastName)" แล้วกำชับอีกว่า "ถ้าสองอย่างนี้เปลี่ยน ให้ลุกไปหยิบกระดาษใบใหม่มาเขียนชื่อเต็ม (setFull) แล้วมาส่งผมนะ" (เสียเวลาเดิน 2 รอบ)
- Derived State: คุณสั่งเลขาแค่ "จดชื่อจริงกับนามสกุลไว้ทีนะ" แล้วตอนคุณถามว่าชื่อเต็มคืออะไร เลขาสามารถ "ตะโกนบอกได้ทันที" โดยไม่ต้องลุกไปเขียนกระดาษใหม่ (คำนวณตอน Render เลย)
🚀 3. Execution Journey: เส้นทางของการ "เกิดใหม่" (Re-render)
เมื่อมีการคลิกปุ่มหรือพิมพ์ข้อมูล นี่คือสิ่งที่ React ทำ:
🛠 Step-by-step:
- Trigger: State เปลี่ยน
- Re-calculating: React รันฟังก์ชันซ้ำทั้งก้อน เพื่อหาค่าใหม่ (ตอนนี้แหละที่ Derived State ทำงาน)
- Comparing: ถ้าผลลัพธ์เหมือนเดิม (Referential Equality) มันจะหยุดทำต่อ
- Updating: ถ้าต่างกัน มันถึงจะไปแตะ Real DOM
// ✅ Best Practice: ใช้ Functional Update เพื่อลดความยุ่งเหยิงของ Dependency
const addCount = useCallback(() => {
setCount((prev) => prev + 1); // 🛠 ไม่ต้องดึง count เข้ามาใน [] ของ useCallback ให้มันรันใหม่มั่วซั่ว
}, []);
🪤 4. The Junior Trap: โรค "บ้า useMemo"
จูเนียร์มักจะคิดว่าใส่ useMemo ครอบทุกอย่างแล้วจะเร็ว:
// ❌ Junior Trap: ใช้ useMemo กับเรื่องขี้ผง
const isShow = useMemo(() => active && visible, [active, visible]);
ทำไมถึงช้าลง? เพราะ React ต้องเสียแรงไปจำค่าเก่า (Memory) และเสียแรงมานั่งวน Loop เช็คค่าใน [] (CPU) เพื่อเปรียบเทียบว่า "เปลี่ยนหรือยัง?" ซึ่งงานที่ทำใน useMemo ดันง่ายกว่าการที่ React นั่งเช็คซะอีก!
✅ การแก้ไข: ใช้ useMemo กับการ Filter/Sort ข้อมูลหลักพันตัวเท่านั้น หรือเมื่อต้องการรักษา "ที่อยู่" ของ Object เพื่อส่งให้ลูกที่ทำ React.memo ไว้
⚖️ 5. The Why Matrix: ท่าไหนคุ้มค่ากว่ากัน?
| เทคนิค | ความยาก | ผลลัพธ์ที่ได้ | เมื่อไหร่ที่ควรใช้ |
|---|---|---|---|
| Derived State | ⭐️ | ⚡⚡⚡ เร็วมาก | ตลอดเวลาที่ทำได้ |
| Common Lift Up | ⭐️⭐️ | ⚡⚡ ลด Render ลูก | เมื่อ State ใช้ร่วมกันหลายจุด |
| useMemo / useCallback | ⭐️⭐️⭐️ | ⚡ (มี overhead) | งานคำนวณหนักๆ (Loop เยอะ) |
| React.memo | ⭐️⭐️ | ⚡⚡ | Component ลูกที่มีความซับซ้อนสูง |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการรู้ว่า "โค้ดที่มี Performance ดีที่สุด คือโค้ดที่ไม่ต้องรัน" ดังนั้นอย่าพยายามหาทางทำให้ useEffect รันเร็วขึ้น แต่จงหาทางลบมันทิ้งแล้วใช้คำนวณสดๆ (Derived State) แทนครับ!