พจนานุกรมปูพื้นฐานสมอง React Hooks (Hooks Dictionary)
ปูพื้นฐานคำศัพท์หัวใจสำคัญในการจัดการสเตตัสความทรงจำและการเฝ้าระวังสิ่งแวดล้อม:
กลุ่มฟังก์ชันพิเศษที่ขึ้นต้นด้วย use (เช่น useState, useEffect) ที่ทำหน้าที่คล้ายเบ็ดโยนไปเกี่ยวพลังจำและวงจรของ React Engine มาให้ฟังก์ชันปกติใช้งาน
ตัวแปรพิเศษที่ทำหน้าที่เปรียบเหมือนสมุดบันทึกใน React Engine ซึ่งหากค่าของข้อมูลนี้มีการเปลี่ยนแปลง จะสั่งสั่งให้หน้าเว็บวาดตัวเองอัปเดตใหม่ทันที
การทำงานส่วนเสริมที่นอกเหนือจากการวาดปุ่มและดีไซน์หลัก เช่น การเชื่อมต่อดึงข้อมูล API ภายนอก หรือการแอบสั่งพิมพ์ข้อความลง Log หลังบ้าน
รายการตัวแปรที่ใส่ท้ายคำสั่ง useEffect เพื่อกำหนดขอบเขตควบคุมว่าหากตัวแปรเหล่านั้นเปลี่ยนแปลงเท่านั้น จึงจะสั่งรันโค้ดประมวลผล Side Effect ซ้ำอีกรอบ
1ทำไมฟังก์ชันธรรมดาถึงต้องมี Hooks? 🤔
ในยุคก่อนหน้านี้ การทำให้ Component จดจำข้อมูลบางอย่างได้ เราต้องเขียนโค้ดด้วยรูปแบบ Class Component ที่เข้าใจยากและยุ่งเหยิงมาก แต่ใน React ยุคใหม่ เราเขียน Component ด้วย JavaScript Function ธรรมดาๆ แทน เพื่อความคล่องตัวและกระชับของโค้ด
ทว่า... ในโลกของโปรแกรมมิ่ง JavaScript ฟังก์ชันทั่วไปมีคุณสมบัติหนึ่งที่เป็นอุปสรรคต่อการทำ UI นั่นคือ **"ความจำเสื่อมชั่วคราว (Statelessness)"** เมื่อฟังก์ชันทำงานบรรทัดสุดท้ายเสร็จและ return หน้าจอ HTML ออกมาแล้ว **ตัวแปรภายในทั้งหมดจะสลายหายไปจากหน่วยความจำทันที!** และเมื่อมีการเรนเดอร์ใหม่ มันก็จะเริ่มต้นนับหนึ่งใหม่เสมอ
เปรียบเทียบให้เห็นภาพ: ปลาทอง กับ สมุดโน้ตกันน้ำ 📝🐠
ทุกๆ ครั้งที่เกิดการวาดหน้าจอใหม่ (Re-render) ฟังก์ชันจะถูกสั่งให้รันตั้งแต่บรรทัดแรกใหม่หมด เปรียบเหมือนปลาทองที่ว่ายน้ำวนกลับมาที่เดิมแล้ว **ลืมทุกอย่างที่เกิดขึ้นก่อนหน้า** ไม่เหลือความจำเดิมอยู่เลย
Hooks จะทำหน้าที่เหมือน **"เบ็ด"** ที่โยนออกไปนอกตัวปลาทอง เพื่อไป **"เกี่ยว" เอาสมุดโน้ตที่อยู่นอกตัวฟังก์ชัน (ใน React Engine)** กลับมาอ่าน ทำให้รู้ว่าก่อนหน้านี้เคยจดอะไรไว้ และอัปเดตข้อมูลนั้นได้อย่างต่อเนื่อง
ทำไมตัวแปรปกติถึง "อัปเดตหน้าจอเว็บไม่ได้"?
ลองจินตนาการว่าคุณสร้างตัวแปรธรรมดา let count = 0; ไว้ในฟังก์ชัน แล้วกดเพิ่มค่าผ่าน count++:
- ค่าในหน่วยความจำ RAM เปลี่ยนแปลงจริง: ตัวแปรกลายเป็น 1, 2, 3... จริงๆ
- แต่หน้าจอไม่เปลี่ยน: เพราะ React ไม่รู้เรื่องเลย! ไม่มีกลไกใดส่งสัญญาณบอก React Engine ว่า "เฮ้! ค่าเปลี่ยนแล้วนะ ช่วยวาดรูป (Re-render) หน้าจอใหม่ให้ผู้ใช้เห็นหน่อย"
- เมื่อถูกรีเรนเดอร์จากสาเหตุอื่น: ตัวแปร
let count = 0;จะโดนประกาศทับและรีเซ็ตค่ากลับไปเป็น0ทันทีเหมือนไม่เคยมีอะไรเกิดขึ้น!
📊 ตารางเปรียบเทียบ: ตัวแปรปกติ vs ตัวแปรสถานะของ React (State)
| คุณสมบัติ | ตัวแปรปกติ (let / const) | ตัวแปรสถานะ React (State) |
|---|---|---|
| การเก็บรักษาค่า (Persistence) | สลายหายไปทันทีเมื่อรันฟังก์ชันเสร็จ หรือโดนล้างเมื่อมี Re-render | React ช่วยจำไว้ในสมองส่วนกลางอย่างปลอดภัย ไม่หายไปไหน |
| การสั่งวาดหน้าจอใหม่ (Re-render) | ❌ ไม่ส่งผลใดๆ ต่อการแสดงผลหน้าเว็บ | ✅ สั่งให้ React วาดหน้าจออัปเดตใหม่ทันทีที่เปลี่ยนค่า |
| วิธีเรียกใช้งาน | let count = 0; | const [count, setCount] = useState(0); |
| วิธีอัปเดตค่า | count = count + 1; | setCount(count + 1); |
นี่จึงเป็นเหตุผลที่ React สร้าง **Hooks (เบ็ดเกี่ยว)** ขึ้นมา! มันคือกลุ่มฟังก์ชันพิเศษที่ขึ้นต้นด้วยคำว่า use(เช่น useState, useEffect) ซึ่งออกแบบมาเพื่อให้เราสามารถ **"โยนเบ็ดไปเกี่ยวเบื้องหลังการทำงานหลักของ React Engine"** เพื่อหยิบยืมพลังพิเศษ เช่น ระบบความทรงจำระยะยาว (State) และระบบความเคลื่อนไหว (Lifecycle) มาสวมใส่ให้กับฟังก์ชันธรรมดาของเราได้อย่างสมบูรณ์แบบและทรงพลัง! 🚀
2useState() — เบ็ดกล่องความจำของ UI
เบ็ดตัวแรกและเป็นตัวที่เราหยิบมาใช้บ่อยที่สุดก็คือ useState หน้าที่ของมันคือ "สร้างกล่องเก็บสถานะในสมองของ Component"
ไวยากรณ์ในการเขียนแสนเรียบง่าย แต่มีเอกลักษณ์ดังนี้:
ตัวอย่างการประกาศ useStateimport { useState } from "react"; function Counter() { // 1. ประกาศตัวแปรจดจำสถานะชื่อ count และฟังก์ชันอัปเดตชื่อ setCount const [count, setCount] = useState(0); // เริ่มต้นด้วยเลข 0 return ( <div> <p>คุณกดคลิกไปแล้ว {count} ครั้ง</p> {/* 2. สั่งเปลี่ยนค่าผ่าน setCount เพื่อบอก React ให้วาดหน้าจอใหม่ */} <button onClick={() => setCount(count + 1)}> เพิ่มแต้ม </button> </div> ); }
💡 สังเกตการตั้งชื่อ: เรานิยมใช้โครงสร้าง [name, setName] เสมอ เช่น [isOn, setIsOn] หรือ [user, setUser] เพื่อความเข้าใจที่ชัดเจนตรงกันทั้งโลก
ทดลองเล่นกับสนามเด็กเล่นจำลอง (Interactive Playground)
ลองสลับไปที่แถบ 1. useState เพื่อควบคุมสวิตช์ไฟและพลังเตาปฏิกรณ์ด้านล่างนี้ แล้วดูการจำลองการเปลี่ยนค่าของ State และการวาดภาพ Render บนหน้าจอจริงได้เลย!
คอนโทรลรูม (Control Panel)
โค้ดที่รันอยู่เบื้องหลัง (Behind the Scenes)
const [isOn, setIsOn] = useState(false);
const [theme, setTheme] = useState("cyan");
เมื่อ State เปลี่ยนแปลง เตาปฏิกรณ์จะเรนเดอร์ภาพใหม่ทันที!
3useEffect() — ตากล้องที่คอยดักดูความเคลื่อนไหว
เมื่อ Component ของเรามีสมองเอาไว้จำสิ่งต่างๆ แล้ว (ด้วย useState) บางทีเราอาจจะอยากให้เกิด "ผลลัพธ์ข้างเคียง (Side Effects)" นอกเหนือจากการแสดงผล HTML ปกติ เช่น:
- "เมื่อหลอดไฟเปิด ให้บันทึกประวัติเหตุการณ์เก็บไว้ในตาราง"
- "เมื่อเริ่มเปิดหน้าเว็บครั้งแรกสุด ให้ไปดึงข้อมูลราคาน้ำมันจาก API"
- "เมื่อค่าพลังงานเปลี่ยน ให้แก้ไขหัวข้อบนแท็บเว็บบนสุด (Document Title)"
งานเหล่านี้เราจะทำผ่านเบ็ดตัวที่สองคือ useEffect ซึ่งมีไวยากรณ์ในการเขียนดังนี้:
โครงสร้าง useEffectuseEffect(() => { // 📸 โค้ดส่วนที่จะให้ทำงาน (Side Effect) console.log("พลังงานอัปเดตเป็น: " + power); return () => { // 🧹 (ตัวเลือกพิเศษ) ล้างข้อมูลส่วนเกิน (Cleanup Function) }; }, [power]); // 👁️ อาร์เรย์เฝ้าระวังตัวแปร (Dependency Array)
4ความลับของ Dependency Array 👁️
หัวใจที่ทำให้หลายคนงุนงงกับ useEffect มากที่สุดคือกล่องเหลี่ยมๆ ข้างหลังที่เรียกว่า Dependency Array (ชุดพารามิเตอร์เฝ้าระวัง)ซึ่งมีรูปแบบการส่งค่าเข้าไปอยู่ 3 แบบหลักๆ และให้ผลลัพธ์ต่างกันราวฟ้ากับเหว:
อาร์เรย์ว่างเปล่า
รันคำสั่ง เพียงครั้งเดียวเท่านั้น ตอนที่ Component ปรากฏตัวบนหน้าจอครั้งแรกสุด (Mounting) เหมาะมากสำหรับการยิง API โหลดข้อมูลครั้งแรก
ระบุตัวแปรที่เฝ้า
รันคำสั่งครั้งแรก และจะรันซ้ำ ทุกครั้งที่ตัวแปรที่อยู่ข้างในกล่องมีการเปลี่ยนค่า เช่น เฝ้าพลังงาน เมื่อพลังงานเปลี่ยนก็จะรันฟังก์ชันทันที
ละเว้นอาร์เรย์
⚠️ อันตราย! โค้ดข้างในจะรัน ทุกๆ ครั้งที่มีการเรนเดอร์ภาพใหม่ (ไม่ว่าใครเปลี่ยนก็ตาม) อาจส่งผลให้เว็บค้างได้ถ้าไปสั่งแก้ State ด้านในวนซ้ำ (Infinity Loop)
💡 คำแนะนำ: ลองสลับไปเล่นที่สนามเด็กเล่นด้านบนในแถบ 2. useEffect ดูอีกครั้ง ลองเลือกติ๊กถูก/ติ๊กออกในกล่อง "เฝ้า [power]" หรือ "เฝ้า [isOn]" แล้วทดลองเปลี่ยนค่าด้านซ้ายดู จะช่วยให้มองเห็นความสัมพันธ์ของ Dependency Array ได้ชัดเจนยิ่งขึ้น!
5กฎเหล็กของการใช้ Hooks 📜
เพื่อรักษาความมั่นคงและความถูกต้องในความทรงจำของ React เขาจึงตั้งกฎเหล็กไว้ 2 ข้อที่ห้ามโปรแกรมเมอร์ละเมิดเด็ดขาด:
ประกาศที่ระดับบนสุด (Only Call Hooks at the Top Level)
ห้ามเรียกใช้ Hooks ข้างในลูป (for), ข้างในคำสั่งเงื่อนไข (if) หรือฟังก์ชันย่อยซ้อนทับ เพราะ React จดจำสถานะตามลำดับการเรียกใช้งาน (Order)
เรียกจาก React เท่านั้น (Only Call Hooks from React Functions)
ห้ามเรียกใช้ Hooks ในไฟล์ JavaScript ทั่วไป ต้องเรียกใช้จากฟังก์ชันที่เป็น React Component หรือ Custom Hook เท่านั้น
