useRef คือฮุก React ที่สร้างออบเจ็กต์ ref ที่เปลี่ยนแปลงได้พร้อมคุณสมบัติ .current ซึ่งคงอยู่ระหว่างการเรนเดอร์ของคอมโพเนนต์ แตกต่างจาก useState การเปลี่ยนแปลง .current จะไม่ทำให้เกิดการเรนเดอร์ซ้ำ ซึ่งทำให้ useRef เหมาะสำหรับการจัดเก็บการอ้างอิง DOM ตัวจับเวลา และค่าใด ๆ ที่ต้องคงอยู่ระหว่างการเรนเดอร์โดยไม่ต้องวาด UI ใหม่ ตาม เอกสาร React (2025) ออบเจ็กต์ ref จะถูกสร้างขึ้นครั้งเดียวตลอดอายุของคอมโพเนนต์และไม่เปลี่ยนแปลงระหว่างการเรนเดอร์ ซึ่งรับประกันความเสถียรของการอ้างอิง
ประเด็นสำคัญ
useRef คือฮุกที่เพิ่มใน React 16.8 ซึ่งส่งคืนออบเจ็กต์ ref ที่เปลี่ยนแปลงได้พร้อมคุณสมบัติ .current เดียว ค่าเริ่มต้นจะถูกกำหนดให้ .current เมื่อคอมโพเนนต์ถูกเมาท์ ความแตกต่างหลักจากตัวแปรทั่วไปคือออบเจ็กต์ ref อยู่รอดจากการเรนเดอร์ซ้ำ: ในการเรนเดอร์ครั้งถัดไป React จะไม่สร้าง ref ใหม่แต่จะส่งคืนออบเจ็กต์เดิม
ภายใน useRef ใช้กลไกเดียวกับ useState และ useEffect — รายการเชื่อมโยงของฮุกบนโหนด fiber อย่างไรก็ตาม แตกต่างจาก useState React ไม่ติดตามการเปลี่ยนแปลงของออบเจ็กต์ ref ไม่จัดคิวอัปเดต และไม่กำหนดเวลาการเรนเดอร์ซ้ำ ทำให้ useRef เป็นฮุกที่เบาอย่างยิ่ง ไม่ส่งผลกระทบต่อประสิทธิภาพแม้ภายใต้การเปลี่ยนแปลงบ่อยครั้ง
ตาม ทีม React — Hooks FAQ (2025) useRef โดยพื้นฐานแล้วคือ “กล่อง” ที่เก็บค่า น้ำตาลเชิงไวยากรณ์: const ref = useRef(ค่าเริ่มต้น) เทียบเท่ากับ const ref = { current: ค่าเริ่มต้น } โดยมีความแตกต่างที่ React รับประกันว่าออบเจ็กต์ ref จะเหมือนเดิมในทุกการเรนเดอร์ ไม่มีเวทมนตร์ — เพียงแค่ การอ้างอิงที่เสถียร ที่จัดการโดย React
import { useRef } from 'react';
function Component() {
const countRef = useRef(0);
// countRef.current = 0 ในตอนแรก
// countRef.current = 5 หลังการเปลี่ยนแปลง
// ไม่มีการเรนเดอร์ซ้ำเกิดขึ้น!
}
กรณีการใช้งานที่พบบ่อยที่สุดของ useRef คือการเข้าถึงองค์ประกอบ DOM โดยตรง React กำหนดให้ elementRef.current เป็นการอ้างอิงไปยังโหนด DOM หลังจากคอมโพเนนต์ถูกเมาท์ ซึ่งจำเป็นสำหรับการโฟกัสอินพุต การวัดขนาดขององค์ประกอบ การรวมเข้ากับไลบรารีแอนิเมชันและไลบรารีที่ไม่ใช้การเรนเดอร์ของ React
React จัดการการอ้างอิง ref บน DOM โดยอัตโนมัติ: เมื่อเมาท์จะกำหนดโหนด DOM จริง เมื่ออันเมาท์จะตั้งเป็น null ซึ่งรับประกันว่า .current จะมีค่าที่อัปเดตอยู่เสมอ และป้องกันการรั่วไหลของหน่วยความจำผ่านการอ้างอิงที่ค้างอยู่กับองค์ประกอบที่ถูกลบ ไม่จำเป็นต้องทำความสะอาดด้วยตนเอง
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// โฟกัสอินพุตหลังจากคอมโพเนนต์ถูกเมาท์
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
ตาม เอกสาร React — Refs และ DOM (2025) อย่าใช้ ref สำหรับการดำเนินการเชิงประกาศ — การเปิด/ปิด modal การจัดการการมองเห็น สำหรับวัตถุประสงค์เหล่านี้มี state และ props Refs มีไว้สำหรับการดำเนินการเชิงจำเป็นที่ไม่สามารถแสดงออกในเชิงประกาศได้: การโฟกัส การเลือกข้อความ การรวมเข้ากับไลบรารีของบุคคลที่สาม
กรณีการใช้งานที่สำคัญอันดับสองคือ การจัดเก็บค่าต่าง ๆ ที่ต้องคงอยู่ระหว่างการเรนเดอร์ แต่การเปลี่ยนแปลงไม่ควรทำให้เกิดการเรนเดอร์ซ้ำ ตัวอย่างทั่วไป: ตัวระบุตัวจับเวลา (setInterval/setTimeout), ธงยกเลิกคำขอ, ค่า prop ก่อนหน้าสำหรับการเปรียบเทียบ และข้อมูลใด ๆ ที่ไม่ส่งผลต่อเอาต์พุต UI
สิ่งนี้มีประโยชน์อย่างยิ่งใน useEffect: useRef จัดเก็บตัวระบุตัวจับเวลาและ cleanup จะลบมัน หาก timerId ถูกจัดเก็บใน useState การเรียก setTimerId แต่ละครั้งจะทำให้เกิดการเรนเดอร์ซ้ำที่ไม่จำเป็น ซึ่งไม่จำเป็นทั้งสำหรับตรรกะและ UI useRef แก้ปัญหานี้โดยไม่มีโอเวอร์เฮดและไม่มี การเรนเดอร์ซ้ำที่ไม่จำเป็น
function Timer() {
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
console.log('ติ๊ก');
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
useEffect(() => stop, []);
return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}
ภาวะที่กลืนไม่เข้าคายไม่ออกหลัก — การเลือกระหว่าง useRef และ useState — แก้ไขได้ด้วยคำถามเดียว: “จำเป็นต้องเรนเดอร์ซ้ำเมื่อค่าเปลี่ยนแปลงหรือไม่?” ถ้าใช่ — useState ถ้าไม่ — useRef useState จัดเก็บสถานะที่ส่งผลต่อเอาต์พุตของคอมโพเนนต์ useRef จัดเก็บข้อมูลที่จำเป็นสำหรับตรรกะภายในแต่ไม่ส่งผลต่อ UI
ในทางปฏิบัติ นักพัฒนามักใช้ useRef เพื่อจัดเก็บ callback เพื่อหลีกเลี่ยงปัญหากับ closure ตัวอย่างเช่น หาก useEffect สมัครรับอีเวนต์และ callback ต้องการ state ปัจจุบัน — จัดเก็บ callback ใน useRef ในการเรนเดอร์แต่ละครั้ง อัปเดต ref.current ด้วยฟังก์ชันใหม่ และเอฟเฟกต์จะเรียก callback ใหม่เสมอโดยไม่ต้องสมัครใหม่
| เกณฑ์ | useRef | useState |
|---|---|---|
| การเรนเดอร์ซ้ำ | ไม่ทำให้เกิดเมื่อเปลี่ยนแปลง | ทำให้เกิดในทุก setState |
| การเปลี่ยนแปลง | โดยตรง: ref.current = ค่า | ผ่าน setter: setState(ค่า) |
| การใช้ใน JSX | ไม่ได้ใช้ (ไม่มีผล) | ใช้ในเอาต์พุตของคอมโพเนนต์ |
| ตัวอย่าง | ตัวจับเวลา การอ้างอิง DOM ค่าก่อนหน้า | ข้อมูลฟอร์ม สถานะ UI ธง |
มี anti-pattern: การใช้ useRef สำหรับข้อมูลที่จำเป็นใน JSX แต่การเปลี่ยนแปลงไม่ควรทำให้เกิดการเรนเดอร์ซ้ำ สิ่งนี้นำไปสู่การไม่ซิงค์กัน — UI แสดงข้อมูลเก่าในขณะที่ ref.current ใหม่แล้ว หากค่าแสดงใน UI — ให้ใช้ useState หากใช้ภายในเท่านั้น — useRef
การรวมกันของ useRef + useEffect เป็นรูปแบบมาตรฐานสำหรับการติดตามค่า prop ก่อนหน้า จัดเก็บค่าก่อนหน้าใน ref เปรียบเทียบกับค่าปัจจุบันใน useEffect และตัดสินใจตามความแตกต่าง ซึ่งมีประโยชน์อย่างยิ่งในแอนิเมชันเมื่อคุณจำเป็นต้องรู้ว่าค่าก่อนการเปลี่ยนแปลงคืออะไร
function PriceDisplay({ price }) {
const prevPriceRef = useRef(price);
useEffect(() => {
const prevPrice = prevPriceRef.current;
if (price > prevPrice) {
animateUp();
} else if (price < prevPrice) {
animateDown();
}
prevPriceRef.current = price;
}, [price]);
return <span>${price}</span>;
}
ตาม เอกสาร React — Hooks FAQ (2025) รูปแบบนี้เรียกว่า “previous value pattern” มันทำงานเพราะ useRef รักษาค่าระหว่างการเรนเดอร์ และ useEffect ทำงานหลังจากการเปลี่ยนแปลงถูกยืนยัน อันดับแรก DOM จะถูกอัปเดตด้วย price ใหม่ จากนั้น useEffect เปรียบเทียบกับค่าก่อนหน้า (ซึ่งยังอยู่ใน ref) และอัปเดต ref.current เป็นค่าปัจจุบัน
ข้อผิดพลาดที่พบบ่อยที่สุดคือ การอ่าน ref.current ในระหว่างเฟสเรนเดอร์ เพื่อคำนวณ JSX เนื่องจากการเปลี่ยนแปลง ref.current ไม่ทำให้เกิดการเรนเดอร์ซ้ำ คอมโพเนนต์อาจใช้ค่าที่ล้าสมัย หาก .current เกี่ยวข้องกับเอาต์พุต UI — ให้ใช้ useState หากคุณต้องการซิงค์ ref และ state ให้ใช้ useEffect เพื่ออัปเดต state จาก ref
// ❌ อย่าอ่าน ref.current ระหว่างเรนเดอร์เพื่อแสดงผล
function BadComponent() {
const valRef = useRef(0);
return <p>{valRef.current}</p>; // จะไม่ถูกอัปเดตเมื่อมีการเปลี่ยนแปลง
}
// ✅ ใช้ state เพื่อแสดงผล, ref สำหรับตรรกะ
function GoodComponent() {
const [val, setVal] = useState(0);
const valRef = useRef(0);
return <p>{val}</p>;
}
ข้อผิดพลาดทั่วไปอีกประการคือการใช้ useRef เป็นวิธีเดียวในการจัดเก็บสถานะในคอมโพเนนต์เมื่อข้อมูลส่งผลต่อ UI จริง ๆ นักพัฒนาเลือก useRef เพื่อหลีกเลี่ยงการเรนเดอร์ซ้ำที่ “ไม่จำเป็น” แต่สุดท้ายก็ได้ UI ที่ไม่อัปเดต วิธีการที่ถูกต้อง: ใช้ useState สำหรับข้อมูล UI และใช้ useRef เฉพาะสำหรับค่าช่วยเหลือที่ไม่มีส่วนร่วมในการเรนเดอร์
คำถามที่พบบ่อย
ได้ นี่เป็นรูปแบบที่พบบ่อย — รูปแบบสถานะก่อนหน้า สร้าง ref และอัปเดตใน useEffect ทุกครั้งที่ค่าเป้าหมายเปลี่ยนแปลง ระหว่างการเรนเดอร์และเอฟเฟกต์ ref.current จะมีค่าก่อนหน้า ซึ่งสามารถเปรียบเทียบกับค่าปัจจุบันได้ สิ่งนี้ไม่ต้องการการเรนเดอร์ซ้ำเพิ่มเติมและทำงานกับข้อมูลทุกประเภท
React กำหนด ref.current เป็นค่าโหนด DOM หลังจากที่องค์ประกอบถูกเรนเดอร์และเพิ่มลงใน DOM จริงเท่านั้น ระหว่างการเรนเดอร์ครั้งแรก คอมโพเนนต์ยังไม่ถูกเมาท์ ดังนั้น ref.current จึงเท่ากับค่าเริ่มต้น (null) การเข้าถึง DOM ผ่าน ref.current ทำได้เฉพาะใน useEffect หรือในตัวจัดการอีเวนต์ที่เรียกหลังจากเมาท์
createRef สร้างออบเจ็กต์ ref ใหม่ในทุกการเรนเดอร์ — ควรใช้เฉพาะในคลาสคอมโพเนนต์เท่านั้น useRef สร้าง ref ครั้งเดียวและส่งคืนออบเจ็กต์เดิมในการเรนเดอร์ครั้งต่อ ๆ ไป ในฟังก์ชันคอมโพเนนต์ให้ใช้เฉพาะ useRef; createRef จะทำให้สูญเสียค่าเมื่อเรนเดอร์ซ้ำเนื่องจากออบเจ็กต์จะถูกสร้างใหม่
ได้ ออบเจ็กต์ ref สามารถส่งเป็น prop ปกติได้: <Child inputRef={inputRef} /> คอมโพเนนต์ลูกใช้มันผ่าน inputRef.current อย่างไรก็ตาม ในการส่ง ref ไปยังองค์ประกอบ DOM ของคอมโพเนนต์ลูกโดยตรง ให้ใช้ forwardRef — HOC ที่อนุญาตให้ส่งต่อ ref ผ่าน props หากไม่มี forwardRef ref จะไม่ถูกผูกกับองค์ประกอบ DOM ภายในคอมโพเนนต์ลูกโดยอัตโนมัติ
ผูก ref กับองค์ประกอบ จากนั้นใน useEffect อ่าน ref.current.getBoundingClientRect() หรือ ref.current.offsetWidth / offsetHeight สำหรับการติดตามการเปลี่ยนแปลงขนาดแบบรีแอกทีฟ ให้ใช้ ResizeObserver ภายใน useEffect: สร้าง observer สมัครรับการเปลี่ยนแปลง และอัปเดต state ด้วยขนาดใหม่ อย่าลืมตัดการเชื่อมต่อ observer ใน cleanup
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม