useRef — คืออะไร ฮุกการอ้างอิงและการทำงานกับ DOM ใน React

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-04 เวลาอ่าน: 9 นาที

useRef คือฮุก React ที่สร้างออบเจ็กต์ ref ที่เปลี่ยนแปลงได้พร้อมคุณสมบัติ .current ซึ่งคงอยู่ระหว่างการเรนเดอร์ของคอมโพเนนต์ แตกต่างจาก useState การเปลี่ยนแปลง .current จะไม่ทำให้เกิดการเรนเดอร์ซ้ำ ซึ่งทำให้ useRef เหมาะสำหรับการจัดเก็บการอ้างอิง DOM ตัวจับเวลา และค่าใด ๆ ที่ต้องคงอยู่ระหว่างการเรนเดอร์โดยไม่ต้องวาด UI ใหม่ ตาม เอกสาร React (2025) ออบเจ็กต์ ref จะถูกสร้างขึ้นครั้งเดียวตลอดอายุของคอมโพเนนต์และไม่เปลี่ยนแปลงระหว่างการเรนเดอร์ ซึ่งรับประกันความเสถียรของการอ้างอิง

ประเด็นสำคัญ

  • useRef — ฮุกสำหรับสร้างออบเจ็กต์ ref ที่เปลี่ยนแปลงได้พร้อมฟิลด์ .current
  • ไม่มีการเรนเดอร์ซ้ำ — การเปลี่ยน .current ไม่ทำให้เกิดการเรนเดอร์คอมโพเนนต์ใหม่
  • การอ้างอิง DOM — ส่งไปยัง prop ref ขององค์ประกอบเพื่อเข้าถึงโหนด DOM โดยตรง
  • การจัดเก็บค่า — เหมาะสำหรับตัวจับเวลา ค่าก่อนหน้า และข้อมูลใด ๆ ระหว่างการเรนเดอร์
  • การอ้างอิงที่เสถียร — ออบเจ็กต์ ref ไม่เปลี่ยนแปลงระหว่างการเรนเดอร์ ต่างจาก closure

useRef ใน React คืออะไร

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

jsx
import { useRef } from 'react';

function Component() {
    const countRef = useRef(0);
    // countRef.current = 0 ในตอนแรก
    // countRef.current = 5 หลังการเปลี่ยนแปลง
    // ไม่มีการเรนเดอร์ซ้ำเกิดขึ้น!
}

การเข้าถึงองค์ประกอบ DOM ด้วย useRef

กรณีการใช้งานที่พบบ่อยที่สุดของ useRef คือการเข้าถึงองค์ประกอบ DOM โดยตรง React กำหนดให้ elementRef.current เป็นการอ้างอิงไปยังโหนด DOM หลังจากคอมโพเนนต์ถูกเมาท์ ซึ่งจำเป็นสำหรับการโฟกัสอินพุต การวัดขนาดขององค์ประกอบ การรวมเข้ากับไลบรารีแอนิเมชันและไลบรารีที่ไม่ใช้การเรนเดอร์ของ React

React จัดการการอ้างอิง ref บน DOM โดยอัตโนมัติ: เมื่อเมาท์จะกำหนดโหนด DOM จริง เมื่ออันเมาท์จะตั้งเป็น null ซึ่งรับประกันว่า .current จะมีค่าที่อัปเดตอยู่เสมอ และป้องกันการรั่วไหลของหน่วยความจำผ่านการอ้างอิงที่ค้างอยู่กับองค์ประกอบที่ถูกลบ ไม่จำเป็นต้องทำความสะอาดด้วยตนเอง

jsx
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 แก้ปัญหานี้โดยไม่มีโอเวอร์เฮดและไม่มี การเรนเดอร์ซ้ำที่ไม่จำเป็น

jsx
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: เมื่อใดควรใช้อะไร

ภาวะที่กลืนไม่เข้าคายไม่ออกหลัก — การเลือกระหว่าง useRef และ useState — แก้ไขได้ด้วยคำถามเดียว: “จำเป็นต้องเรนเดอร์ซ้ำเมื่อค่าเปลี่ยนแปลงหรือไม่?” ถ้าใช่ — useState ถ้าไม่ — useRef useState จัดเก็บสถานะที่ส่งผลต่อเอาต์พุตของคอมโพเนนต์ useRef จัดเก็บข้อมูลที่จำเป็นสำหรับตรรกะภายในแต่ไม่ส่งผลต่อ UI

ในทางปฏิบัติ นักพัฒนามักใช้ useRef เพื่อจัดเก็บ callback เพื่อหลีกเลี่ยงปัญหากับ closure ตัวอย่างเช่น หาก useEffect สมัครรับอีเวนต์และ callback ต้องการ state ปัจจุบัน — จัดเก็บ callback ใน useRef ในการเรนเดอร์แต่ละครั้ง อัปเดต ref.current ด้วยฟังก์ชันใหม่ และเอฟเฟกต์จะเรียก callback ใหม่เสมอโดยไม่ต้องสมัครใหม่

เกณฑ์useRefuseState
การเรนเดอร์ซ้ำไม่ทำให้เกิดเมื่อเปลี่ยนแปลงทำให้เกิดในทุก setState
การเปลี่ยนแปลงโดยตรง: ref.current = ค่าผ่าน setter: setState(ค่า)
การใช้ใน JSXไม่ได้ใช้ (ไม่มีผล)ใช้ในเอาต์พุตของคอมโพเนนต์
ตัวอย่างตัวจับเวลา การอ้างอิง DOM ค่าก่อนหน้าข้อมูลฟอร์ม สถานะ UI ธง

มี anti-pattern: การใช้ useRef สำหรับข้อมูลที่จำเป็นใน JSX แต่การเปลี่ยนแปลงไม่ควรทำให้เกิดการเรนเดอร์ซ้ำ สิ่งนี้นำไปสู่การไม่ซิงค์กัน — UI แสดงข้อมูลเก่าในขณะที่ ref.current ใหม่แล้ว หากค่าแสดงใน UI — ให้ใช้ useState หากใช้ภายในเท่านั้น — useRef

useRef ร่วมกับ useEffect

การรวมกันของ useRef + useEffect เป็นรูปแบบมาตรฐานสำหรับการติดตามค่า prop ก่อนหน้า จัดเก็บค่าก่อนหน้าใน ref เปรียบเทียบกับค่าปัจจุบันใน useEffect และตัดสินใจตามความแตกต่าง ซึ่งมีประโยชน์อย่างยิ่งในแอนิเมชันเมื่อคุณจำเป็นต้องรู้ว่าค่าก่อนการเปลี่ยนแปลงคืออะไร

jsx
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 เป็นค่าปัจจุบัน

ข้อผิดพลาดทั่วไปกับ useRef

ข้อผิดพลาดที่พบบ่อยที่สุดคือ การอ่าน ref.current ในระหว่างเฟสเรนเดอร์ เพื่อคำนวณ JSX เนื่องจากการเปลี่ยนแปลง ref.current ไม่ทำให้เกิดการเรนเดอร์ซ้ำ คอมโพเนนต์อาจใช้ค่าที่ล้าสมัย หาก .current เกี่ยวข้องกับเอาต์พุต UI — ให้ใช้ useState หากคุณต้องการซิงค์ ref และ state ให้ใช้ useEffect เพื่ออัปเดต state จาก ref

  • Ref ใน JSX — การอ่าน ref.current ภายในฟังก์ชันเรนเดอร์นำไปสู่การไม่ซิงค์กันระหว่าง UI และข้อมูล
  • Ref เป็น dependency ของ useEffect — ref.current ไม่ควรอยู่ในอาร์เรย์ dependency; React ไม่ติดตามการเปลี่ยนแปลงของมัน
  • ลืมตรวจสอบ null — เมื่ออันเมาท์ ref.current จะกลายเป็น null; ตรวจสอบก่อนเข้าถึง
  • การจัดเก็บฟังก์ชันใน ref — อัปเดต ref.current ในทุกการเรนเดอร์หากใช้รูปแบบ callback ใหม่
jsx
// ❌ อย่าอ่าน 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 เฉพาะสำหรับค่าช่วยเหลือที่ไม่มีส่วนร่วมในการเรนเดอร์

คำถามที่พบบ่อย

สามารถใช้ useRef เพื่อจัดเก็บสถานะก่อนหน้าได้หรือไม่?

ได้ นี่เป็นรูปแบบที่พบบ่อย — รูปแบบสถานะก่อนหน้า สร้าง ref และอัปเดตใน useEffect ทุกครั้งที่ค่าเป้าหมายเปลี่ยนแปลง ระหว่างการเรนเดอร์และเอฟเฟกต์ ref.current จะมีค่าก่อนหน้า ซึ่งสามารถเปรียบเทียบกับค่าปัจจุบันได้ สิ่งนี้ไม่ต้องการการเรนเดอร์ซ้ำเพิ่มเติมและทำงานกับข้อมูลทุกประเภท

ทำไม ref.current ถึงเป็น null ในการเรนเดอร์ครั้งแรก?

React กำหนด ref.current เป็นค่าโหนด DOM หลังจากที่องค์ประกอบถูกเรนเดอร์และเพิ่มลงใน DOM จริงเท่านั้น ระหว่างการเรนเดอร์ครั้งแรก คอมโพเนนต์ยังไม่ถูกเมาท์ ดังนั้น ref.current จึงเท่ากับค่าเริ่มต้น (null) การเข้าถึง DOM ผ่าน ref.current ทำได้เฉพาะใน useEffect หรือในตัวจัดการอีเวนต์ที่เรียกหลังจากเมาท์

ความแตกต่างระหว่าง useRef และ createRef คืออะไร?

createRef สร้างออบเจ็กต์ ref ใหม่ในทุกการเรนเดอร์ — ควรใช้เฉพาะในคลาสคอมโพเนนต์เท่านั้น useRef สร้าง ref ครั้งเดียวและส่งคืนออบเจ็กต์เดิมในการเรนเดอร์ครั้งต่อ ๆ ไป ในฟังก์ชันคอมโพเนนต์ให้ใช้เฉพาะ useRef; createRef จะทำให้สูญเสียค่าเมื่อเรนเดอร์ซ้ำเนื่องจากออบเจ็กต์จะถูกสร้างใหม่

สามารถส่ง useRef ผ่าน props ได้หรือไม่?

ได้ ออบเจ็กต์ ref สามารถส่งเป็น prop ปกติได้: <Child inputRef={inputRef} /> คอมโพเนนต์ลูกใช้มันผ่าน inputRef.current อย่างไรก็ตาม ในการส่ง ref ไปยังองค์ประกอบ DOM ของคอมโพเนนต์ลูกโดยตรง ให้ใช้ forwardRef — HOC ที่อนุญาตให้ส่งต่อ ref ผ่าน props หากไม่มี forwardRef ref จะไม่ถูกผูกกับองค์ประกอบ DOM ภายในคอมโพเนนต์ลูกโดยอัตโนมัติ

วิธีการวัดขนาดขององค์ประกอบด้วย useRef?

ผูก ref กับองค์ประกอบ จากนั้นใน useEffect อ่าน ref.current.getBoundingClientRect() หรือ ref.current.offsetWidth / offsetHeight สำหรับการติดตามการเปลี่ยนแปลงขนาดแบบรีแอกทีฟ ให้ใช้ ResizeObserver ภายใน useEffect: สร้าง observer สมัครรับการเปลี่ยนแปลง และอัปเดต state ด้วยขนาดใหม่ อย่าลืมตัดการเชื่อมต่อ observer ใน cleanup

สรุป

  • useRef — ฮุกสำหรับสร้างออบเจ็กต์ ref ที่เปลี่ยนแปลงได้ซึ่งคงอยู่ระหว่างการเรนเดอร์โดยไม่ทำให้เกิดการเรนเดอร์ซ้ำ
  • การเข้าถึง DOM — prop ref ถูกส่งไปยังองค์ประกอบเพื่อเข้าถึงโหนด DOM โดยตรง พร้อมใช้งานหลังเมาท์
  • การจัดเก็บข้อมูล — ตัวจับเวลา callback ธง และค่าใด ๆ ที่ไม่ส่งผลต่อ UI ให้จัดเก็บใน ref
  • useRef เทียบกับ useState — เลือก useState หากการเปลี่ยนแปลงควรเรนเดอร์ UI ใหม่; useRef หากไม่
  • รูปแบบค่าก่อนหน้า — จัดเก็บค่าก่อนหน้าใน ref อัปเดตใน useEffect เพื่อเปรียบเทียบ
  • Ref ใน JSX — อย่าอ่าน ref.current ในเฟสเรนเดอร์ สิ่งนี้นำไปสู่การไม่ซิงค์กันของ UI และข้อมูล
  • forwardRef — ใช้เพื่อส่ง ref ผ่าน props ไปยังคอมโพเนนต์ลูก

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม