State Snapshot & Purity
state เป็น snapshot ไม่ใช่ตัวแปรที่มีชีวิต
หัวข้อที่มีชื่อว่า “state เป็น snapshot ไม่ใช่ตัวแปรที่มีชีวิต”นี่คือเรื่องที่คนเข้าใจผิดมากที่สุดใน React ตอนคุณอ่านตัวแปร state คุณกำลังอ่านค่าของตัวเอง สำหรับ render นี้ — snapshot ที่ถูกแช่แข็ง ณ ตอนที่ component ถูกเรียก ไม่เปลี่ยนกลาง render แม้หลังจากคุณเรียก setter แล้ว
function Counter() { const [count, setCount] = useState(0);
function handleClick() { setCount(count + 1); setCount(count + 1); setCount(count + 1); // count is 0 for this whole render. All three read the SAME snapshot (0), // so this sets state to 0 + 1 = 1, three times. Result: 1, not 3. }
return <button onClick={handleClick}>{count}</button>;}ถ้าจะ update จากค่า ล่าสุด ที่ pending อยู่ ให้ส่ง updater function — React รัน updater เหล่านั้นตามลำดับกับค่าที่กำลังเปลี่ยน:
setCount((c) => c + 1);setCount((c) => c + 1);setCount((c) => c + 1);// Each receives the previous pending value: 0→1→2→3. Result: 3.Mental model: re-render คือการเรียกฟังก์ชันของคุณใหม่พร้อม snapshot ใหม่ ตัวแปรของ render เก่าหายไปแล้ว render ใหม่เห็นค่าใหม่
มอง state เป็น immutable
หัวข้อที่มีชื่อว่า “มอง state เป็น immutable”เพราะ state เป็น snapshot ที่ React เทียบด้วย identity คุณจึง ต้องไม่ mutate state ให้สร้าง object/array ใหม่แทน:
// ❌ Mutation — same array reference, React may not re-render, and it's a bug.todos.push(newTodo);setTodos(todos);
// ✅ New reference — React sees a change and re-renders.setTodos([...todos, newTodo]);การ mutate state ทำลายความสามารถของ React ในการตรวจจับการเปลี่ยนแปลง (React เทียบ reference) และทำลาย model แบบ snapshot ให้สร้างค่าใหม่เสมอ
render ต้อง pure
หัวข้อที่มีชื่อว่า “render ต้อง pure”การ render ของ component — ทุกอย่างที่ทำระหว่างคำนวณ JSX ที่จะ return — ต้อง pure: เมื่อ props และ state เท่าเดิม ต้อง return output เดิมและไม่มี side effect ระหว่าง render คุณต้องไม่:
- mutate props, state หรือตัวแปร/object ใด ๆ ที่มีอยู่ก่อน;
- เขียน DOM, ยิง network request หรือเริ่ม timer;
- อ่านหรือเขียนอะไรก็ตามนอกฟังก์ชันที่เปลี่ยนได้
flowchart LR inputs["props + state (snapshot)"] --> render["render (pure): compute JSX only"] render --> out["same inputs → same JSX"] render -. NOT here .-> se["side effects: DOM, network, timers"] se --> eff["→ event handlers & effects"]
Side effect อยู่ใน event handler (สิ่งที่เกิดตอน interaction) และ effect (useEffect สำหรับ sync กับระบบภายนอก) — ไม่ใช่ใน body ของ render purity นี้คือสิ่งที่ทำให้ React เรียก component ของคุณเมื่อไรก็ได้
StrictMode double-render โดยตั้งใจ
หัวข้อที่มีชื่อว่า “StrictMode double-render โดยตั้งใจ”ใน development <StrictMode> ของ React เรียกฟังก์ชัน component ของคุณสองครั้ง โดยตั้งใจ (และรัน effect setup→cleanup→setup) นี่ไม่ใช่ bug — เป็นตัวตรวจจับ ถ้า render ของคุณ pure การเรียกสองครั้งจะให้ output เหมือนกันและไม่มีอะไรพัง ถ้าคุณเผลอ mutate อะไรหรือพึ่ง side effect ระหว่าง render การเรียกซ้ำจะเผยให้เห็น bug ทันทีใน development แทนที่จะเป็น glitch ลึกลับใน production