Kodokon kodokon.com

useRef และการจัดการ DOM

จัดการ DOM และเก็บค่าประจำ instance ด้วย useRef โดยไม่ทำลายความ pure ของการ render

7 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

useRef คืนกล่องที่แก้ไขได้ - object ที่มี property current เพียงตัวเดียว - ซึ่ง identity ของมันอยู่รอดผ่านทุก render ความแตกต่างพื้นฐานสองอย่างจาก state: การแก้ไข ref.current ไม่ trigger การ re-render และการเปลี่ยนแปลงมองเห็นได้ทันทีโดยไม่ต้องรอ render ถัดไป การใช้งานสองกลุ่มจึงตามมา: การอ้างถึง DOM node และการเก็บค่าประจำ instance ที่ต้องอยู่รอดผ่านการ render โดยไม่ขับเคลื่อนการแสดงผล

JSX
import { useEffect, useRef } from "react";

function SearchField() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="search" />;
}
React แนบ node เข้ากับ ref หลังจาก render ถูก commit แล้ว

ในด้านค่าประจำ instance สิ่งใดก็ตามที่ต้องคงอยู่โดยไม่ก่อให้เกิดการ render ควรอยู่ใน ref: id ที่คืนมาจาก setInterval, instance ของไลบรารีภายนอก (แผนที่, editor, video player) หรือค่าจาก render ก่อนหน้า กฎทองยังคงเหมือนเดิม: การ render ต้องเป็นฟังก์ชัน pure ของ props และ state ref ถูกอ่านและเขียนใน effect และ event handler ไม่ใช่ระหว่างการ render

JAVASCRIPT
import { useEffect, useRef } from "react";

function usePrevious(value) {
  const ref = useRef(undefined);

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}
effect รันหลังจาก render: ref จึงตามหลังอยู่หนึ่ง render

ในการเปิดเผย DOM node จาก component ของคุณเอง ให้ส่ง ref ต่อไป: ตั้งแต่ React 19 เป็นต้นมา ref เป็น prop ธรรมดาของ function component; ก่อนหน้านั้น การผ่าน forwardRef เป็นสิ่งจำเป็น สัญชาตญาณสุดท้ายที่ควรฝึก: เลือกวิธีแบบ declarative เมื่อ state เพียงพอที่จะอธิบายผลลัพธ์ (CSS class, attribute disabled) สงวน ref ไว้สำหรับดินแดนที่ React ไม่ครอบคลุม: focus, การเลือกข้อความ, การวัดขนาด element, การเลื่อน, การเล่นสื่อ

JSX
import { useRef } from "react";

function TextField({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

function Form() {
  const fieldRef = useRef(null);

  return (
    <>
      <TextField ref={fieldRef} />
      <button onClick={() => fieldRef.current.focus()}>
        Give focus
      </button>
    </>
  );
}
ตั้งแต่ React 19 เป็นต้นมา ref ถูกส่งเหมือน prop อื่นๆ

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ความแตกต่างพื้นฐานอะไรที่แยก useRef ออกจาก useState ในการเก็บค่า?
    • การแก้ไข ref.current ไม่ trigger การ re-render ต่างจากการอัปเดต state
    • useRef เก็บค่าไว้หลังจาก component ถูก unmount
    • useRef เก็บได้เฉพาะ DOM node เท่านั้น
    • ref ถูกแบ่งปันระหว่าง instance ทั้งหมดของ component
  2. ทำไมจึงเข้าถึง inputRef.current ใน useEffect แทนที่จะเข้าถึงระหว่างการ render?
    • เพราะ useEffect รวมกลุ่มการเข้าถึง DOM เพื่อประสิทธิภาพที่ดีขึ้น
    • เพราะ ref ถูกล็อกไม่ให้อ่านระหว่างการ render
    • เพราะ React แนบ node หลังจาก commit เท่านั้น: ระหว่าง render ครั้งแรก current ยังคงเป็น null
  3. usePrevious(value) คืนค่าอะไรใน render ครั้งแรกสุด?
    • ค่าปัจจุบันของ value
    • undefined เพราะ effect ที่เขียนลงใน ref ยังไม่ได้รัน
    • null
    • ค่าของ render ถัดไป