useState เป็นฮุคในตัวของ React ที่ช่วยให้คอมโพเนนต์แบบฟังก์ชันสามารถจัดการสถานะภายในได้โดยไม่ต้องแปลงเป็นคอมโพเนนต์แบบคลาส React 16.8 ได้แนะนำฮุค และ useState กลายเป็นฮุคที่ใช้มากที่สุดในบรรดาฮุคทั้งหมด แทนที่ this.state และ this.setState จากแนวทางแบบคลาส ตาม เอกสารประกอบ React (2025) คอมโพเนนต์มากกว่า 80% ในแอปพลิเคชัน React สมัยใหม่ใช้ useState เพื่อจัดการข้อมูลฟอร์ม แฟลกอินเทอร์เฟซ และตัวนับ ฮุคจะส่งคืนทูเพิลที่ประกอบด้วยค่าปัจจุบันและฟังก์ชัน setter ที่อัปเดตสถานะและกระตุ้นให้เกิดการเรนเดอร์คอมโพเนนต์ใหม่
ประเด็นสำคัญ
useState เป็นฮุคพื้นฐานของ React ที่เพิ่มเข้ามาในเวอร์ชัน 16.8 ซึ่งให้ความสามารถแก่คอมโพเนนต์แบบฟังก์ชันในการจัดเก็บและเปลี่ยนแปลงสถานะภายใน ก่อนที่จะมีการแนะนำฮุค สถานะสามารถใช้ได้เฉพาะในคอมโพเนนต์แบบคลาสผ่าน this.state และ this.setState ซึ่งทำให้คอมโพเนนต์แบบฟังก์ชันเป็นเพียงการนำเสนอเท่านั้น useState ได้ลบข้อจำกัดนี้ ทำให้สามารถเขียนแอปพลิเคชันทั้งหมดโดยใช้คอมโพเนนต์แบบฟังก์ชัน
ฮุครับอาร์กิวเมนต์เดียว — ค่าสถานะเริ่มต้น — และส่งคืนอาร์เรย์ของสององค์ประกอบ องค์ประกอบแรกคือค่าสถานะปัจจุบัน และองค์ประกอบที่สองคือฟังก์ชันสำหรับอัปเดตมัน React รับประกันว่าฟังก์ชัน setter มีความเสถียรและไม่เปลี่ยนแปลงระหว่างการเรนเดอร์ ทำให้คุณสามารถส่งต่อไปยังคอมโพเนนต์ลูกได้อย่างปลอดภัยและใช้ในคลอเชอร์
ตาม แบบสำรวจการใช้งาน React DevTools (2024) มีการใช้ useState ใน 96% ของแอปพลิเคชัน React ทำให้เป็นฮุคที่พบมากที่สุดในระบบนิเวศ แม้แต่ในแอปพลิเคชันที่ใช้สถานะส่วนกลางผ่าน Redux หรือ Zustand สถานะภายในผ่าน useState ก็ยังคงเป็นกลไกหลักในการจัดการสถานะ UI — โมดัลที่เปิดอยู่ ค่าฟิลด์อินพุต แท็บที่ใช้งานอยู่
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
กลไกภายในของ useState ขึ้นอยู่กับระบบโหนดไฟเบอร์ของ React คอมโพเนนต์แต่ละตัวใน React จะแสดงด้วยโหนดไฟเบอร์ที่เก็บลิสต์連結ของฮุคที่เกี่ยวข้องกับมัน เมื่อคอมโพเนนต์เรียก useState React จะสร้างโหนดใหม่ในลิสต์นี้และเก็บค่าสถานะปัจจุบันและการอ้างอิงไปยังคิวอัปเดตไว้ในนั้น
เมื่อเรียกฟังก์ชัน setter React จะไม่อัปเดตสถานะทันที แต่จะวางการอัปเดตไว้ในคิวสถานะที่รอดำเนินการ กำหนดเวลาการเรนเดอร์คอมโพเนนต์ใหม่ และจะคำนวณสถานะใหม่ในระหว่างการเรนเดอร์ครั้งถัดไปโดยอิงจากสถานะเก่าและการอัปเดตที่ใช้แล้วเท่านั้น สิ่งนี้รับประกัน การจัดกลุ่มเป็นชุด — หากคุณเรียก setter สามครั้งในตัวจัดการเหตุการณ์เดียว React จะจัดกลุ่ม它们เป็นการเรนเดอร์ใหม่ครั้งเดียว
ตาม ทีม React — กลุ่มทำงาน React 18 (2024) การจัดกลุ่มอัตโนมัติถูกขยายใน React 18: การอัปเดตสถานะตอนนี้ถูกจัดกลุ่มไม่เพียงแต่ในตัวจัดการเหตุการณ์ แต่ยังรวมถึงใน setTimeout, คอลแบ็ก Promise และตัวจัดการเหตุการณ์ดั้งเดิมด้วย สิ่งนี้ให้ประสิทธิภาพเพิ่มขึ้นถึง 30% ในสถานการณ์ที่มีการอัปเดตสถานะหลายครั้ง
เมื่อสถานะใหม่ขึ้นอยู่กับสถานะก่อนหน้า ให้ใช้รูปแบบฟังก์ชันของ setter: setCount(prev => prev + 1) React ส่งค่าสถานะจริง ณ เวลาที่อัปเดตไปยังฟังก์ชัน รับประกันความถูกต้องแม้กับการอัปเดตที่จัดกลุ่มเป็นชุด หากไม่มีรูปแบบฟังก์ชัน setCount(count + 1) อาจใช้ค่าที่ล้าสมัยหากถูกเรียกหลายครั้งติดต่อกัน
// การเริ่มต้นแบบขี้เกียจ — ทำงานเพียงครั้งเดียว
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3
// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)
ไวยากรณ์พื้นฐานของ useState นั้นสั้นมาก: const [state, setState] = useState(initialValue) ค่าเริ่มต้นจะใช้เฉพาะในระหว่างการเรนเดอร์ครั้งแรกเท่านั้น ในการเรนเดอร์ครั้งต่อๆ ไป React จะละเว้นมันและส่งคืนค่าที่บันทึกไว้ปัจจุบัน หากการคำนวณเริ่มต้นต้องการการดำเนินการที่ใช้ทรัพยากรมาก ให้ส่งฟังก์ชันเริ่มต้น: useState(() => computeExpensiveInitial())
การเริ่มต้นแบบขี้เกียจมีความสำคัญเป็นพิเศษเมื่อค่าเริ่มต้นมาจาก localStorage การแปลงข้อมูลที่ซับซ้อน หรือการแยกวิเคราะห์พารามิเตอร์ URL React จะเรียกฟังก์ชันเริ่มต้นเพียงครั้งเดียว — เมื่อคอมโพเนนต์ถูกเมานต์ — ซึ่ง ประหยัดทรัพยากร ในการเรนเดอร์ครั้งต่อๆ ไป หากไม่มีการเริ่มต้นแบบขี้เกียจ นิพจน์ที่ใช้ทรัพยากรมากจะถูกประเมินทุกครั้งที่มีการเรนเดอร์ แม้ว่าผลลัพธ์จะถูกละเว้นก็ตาม
| รูปแบบ | ตัวอย่าง | เมื่อใดควรใช้ |
|---|---|---|
| โดยตรง | useState(0) | ค่าเริ่มต้นอย่างง่าย |
| ขี้เกียจ | useState(() => compute()) | การคำนวณเริ่มต้นที่ใช้ทรัพยากรมาก |
| setter แบบฟังก์ชัน | setState(prev => prev + 1) | การอัปเดตตามค่าก่อนหน้า |
| setter โดยตรง | setState(newValue) | ค่าใหม่ไม่ขึ้นอยู่กับค่าเก่า |
// สถานะออบเจ็กต์ — สร้างการอ้างอิงใหม่
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
useState ทำงานได้ดีทั้งกับชนิดข้อมูลดั้งเดิม (ตัวเลข สตริง บูลีน) และชนิดอ้างอิง (อาร์เรย์ ออบเจ็กต์) อย่างไรก็ตาม มีความแตกต่างที่สำคัญ: สำหรับชนิดดั้งเดิม React จะตรวจจับการเปลี่ยนแปลงตามค่า สำหรับออบเจ็กต์และอาร์เรย์ จะตรวจจับตามการอ้างอิง การเปลี่ยนค่าออบเจ็กต์ที่มีอยู่โดยไม่สร้างใหม่จะไม่กระตุ้นให้เกิดการเรนเดอร์ใหม่เนื่องจากการอ้างอิงยังคงเหมือนเดิม
เมื่อทำงานกับออบเจ็กต์และอาร์เรย์ ให้สร้างสำเนาใหม่ที่มีข้อมูลที่แก้ไขแล้วเสมอ สำหรับออบเจ็กต์ ให้ใช้ไวยากรณ์ spread: setUser(prev => ({...prev, name: newName})) สำหรับอาร์เรย์ ให้ใช้เมธอดที่ส่งคืนอาร์เรย์ใหม่: filter, map, concat หรือไวยากรณ์ spread สำหรับการเพิ่ม元素 เมธอดการเปลี่ยนค่าเช่น push, pop, splice จะไม่ทำงาน — React จะละเว้น它们เนื่องจากการอ้างอิงอาร์เรย์ไม่เปลี่ยนแปลง
// ✅ ถูกต้อง: ฮุคที่ระดับบนสุด
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
setForm(prev => ({ ...prev, [field]: value }));
// ...
const [items, setItems] = useState([]);
const addItem = item =>
setItems(prev => [item, ...prev]);
เช่นเดียวกับฮุค React ทั้งหมด useState ปฏิบัติตามกฎสองข้อ ข้อแรก: เรียกฮุคเฉพาะที่ระดับบนสุดของคอมโพเนนต์เท่านั้น — อย่าวางไว้ภายในเงื่อนไข ลูป หรือฟังก์ชันที่ซ้อนกัน สิ่งนี้รับประกันว่าฮุคถูกเรียกในลำดับเดียวกันทุกครั้งที่มีการเรนเดอร์ ซึ่งสำคัญมากสำหรับลิสต์連結ภายในของ React
กฎข้อที่สอง: เรียกฮุคจากคอมโพเนนต์แบบฟังก์ชันของ React หรือฮุคที่กำหนดเองเท่านั้น อย่าเรียก useState จากฟังก์ชันทั่วไป คอลแบ็ก หรือคอมโพเนนต์แบบคลาส การละเมิดกฎเหล่านี้จะนำไปสู่ข้อผิดพลาด Invalid hook call ซึ่ง React จะตรวจจับได้ในขณะรันไทม์
// ❌ ปัญหาคลอเชอร์ที่ล้าสมัย
function GoodComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// ✅ แก้ไขด้วยการอัปเดตแบบฟังก์ชัน
}
// count ล้าสมัย!
function BadComponent() {
if (isLogged) {
const [user, setUser] = useState(null);
}
}
ข้อผิดพลาดที่พบบ่อยที่สุดคือ การเปลี่ยนค่าโดยตรง ของออบเจ็กต์และอาร์เรย์แทนที่จะสร้างการอ้างอิงใหม่ นักพัฒนาคุ้นเคยกับรูปแบบที่เปลี่ยนค่าได้จากคอมโพเนนต์แบบคลาส ซึ่ง this.state.user.name = ‘ชื่อใหม่’ ทำงานได้ (แม้ว่าจะไม่แนะนำก็ตาม) ในคอมโพเนนต์แบบฟังก์ชัน การเปลี่ยนค่าดังกล่าวจะถูกละเว้น: React ไม่เห็นการเปลี่ยนแปลงการอ้างอิงและไม่เรนเดอร์คอมโพเนนต์ใหม่
ปัญหาคลอเชอร์ที่ล้าสมัยเกิดขึ้นเมื่อคอลแบ็กที่ส่งไปยัง useEffect หรือตัวจัดการเหตุการณ์จับค่าสถานะที่ล้าสมัยจากคลอเชอร์ ณ เวลาที่สร้างคอลแบ็ก วิธีแก้ไขคือใช้รูปแบบฟังก์ชันของ setter หรือรวมค่าที่จำเป็นในอาร์เรย์ dependencies ของ useEffect ตาม เอกสารประกอบ React — คำถามที่พบบ่อยเกี่ยวกับฮุค (2025) คลอเชอร์ที่ล้าสมัยเป็นสาเหตุอันดับสองของบั๊กในฮุค รองจากอาร์เรย์ dependencies ที่ไม่ถูกต้อง
// ❌ Stale closure problem
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // count is stale!
}, 1000);
return () => clearInterval(timer);
}, []);
// ✅ Fixed with functional update
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
คำถามที่พบบ่อย
ได้ แต่ต้องใช้ความระมัดระวัง สำหรับออบเจ็กต์ที่ซ้อนกันลึก แต่ละระดับต้องการการคัดลอกระหว่างการอัปเดต: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})) หากคุณมีออบเจ็กต์ที่มีระดับการซ้อนสามระดับขึ้นไป ให้พิจารณา Immer (ไลบรารีสำหรับการอัปเดตแบบไม่เปลี่ยนแปร) หรือแบ่งสถานะออกเป็นหลายการเรียก useState สำหรับระดับต่างๆ
useState เป็นแบบอะซิงโครนัสในแง่ที่ว่าการเรียก setter จะไม่อัปเดตค่าทันที React จะวางการอัปเดตในคิวและดำเนินการในการเรนเดอร์ครั้งถัดไป หลังจาก setCount(newValue) ตัวแปร count ในฟังก์ชันปัจจุบันยังคงมีค่าเก่า ค่าใหม่จะพร้อมใช้งานในการเรียกคอมโพเนนต์ครั้งถัดไปเท่านั้น กลไกการจัดกลุ่มเป็นชุดรับประกันว่าหลายการเรียกจะถูกจัดกลุ่มเป็นการเรนเดอร์ครั้งเดียว
React จะโยนข้อผิดพลาด Invalid hook call ฮุคสามารถเรียกได้ภายในคอมโพเนนต์แบบฟังก์ชันของ React หรือฮุคที่กำหนดเอง (ฟังก์ชันที่ชื่อขึ้นต้นด้วย use) เท่านั้น ข้อจำกัดนี้ถูกสร้างไว้ในการimplementฮุค: พวกมันใช้ทรีไฟเบอร์ของคอมโพเนนต์เพื่อจัดเก็บสถานะ และนอกคอมโพเนนต์ ทรีนี้ไม่พร้อมใช้งาน
useState เหมาะสำหรับสถานะอิสระอย่างง่าย (ตัวนับ ฟิลด์อินพุต แฟลก) useReducer เหมาะกว่าเมื่อสถานะเป็นออบเจ็กต์ที่ซับซ้อนที่มีหลายฟิลด์ ตรรกะการอัปเดตขึ้นอยู่กับประเภทของการกระทำ หรือสถานะถัดไปต้องการการแปลงที่ซับซ้อน กฎที่ใช้ได้จริง: หากคุณมีฟิลด์สถานะที่สัมพันธ์กันมากกว่าสามฟิลด์ ให้ใช้ useReducer
สิ่งนี้ทำเพื่อ ประสิทธิภาพ หาก useState อัปเดตสถานะแบบซิงโครนัส แต่ละการเรียก setter จะกระตุ้นให้เกิดการเรนเดอร์ใหม่ทันที ซึ่งนำไปสู่การวาดซ้ำหลายครั้งภายในตัวจัดการเหตุการณ์เดียว การจัดกลุ่มเป็นชุด ซึ่ง React จัดกลุ่มการอัปเดตและดำเนินการเรนเดอร์ใหม่ครั้งเดียว เป็นการปรับให้เหมาะสมที่นำมาใช้ใน React 16 และปรับปรุงอย่างมีนัยสำคัญใน React 18 ด้วยการจัดกลุ่มอัตโนมัติ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม