คู่มือฉบับสมบูรณ์เกี่ยวกับ React Refs โดยเน้นที่ useRef และ createRef เรียนรู้วิธีและเวลาที่ควรใช้แต่ละตัวเพื่อการจัดการคอมโพเนนต์และการเข้าถึง DOM อย่างมีประสิทธิภาพในแอปพลิเคชันระดับโลก
React Refs: ไขข้อข้องใจความแตกต่างระหว่าง useRef และ createRef
ในโลกของการพัฒนา React ที่ไม่เคยหยุดนิ่ง การจัดการสถานะของคอมโพเนนต์ (component state) และการโต้ตอบกับ Document Object Model (DOM) อย่างมีประสิทธิภาพเป็นสิ่งสำคัญอย่างยิ่ง React Refs เป็นกลไกที่ช่วยให้สามารถเข้าถึงและจัดการ DOM elements หรือ React components ได้โดยตรง สองวิธีหลักในการสร้าง Refs คือ useRef
และ createRef
แม้ว่าทั้งสองจะทำหน้าที่สร้าง Refs เหมือนกัน แต่ก็มีความแตกต่างในการใช้งานและกรณีการใช้งาน คู่มือนี้มีจุดมุ่งหมายเพื่อไขข้อข้องใจเกี่ยวกับสองวิธีนี้ เพื่อให้เกิดความชัดเจนว่าเมื่อใดและอย่างไรควรใช้แต่ละวิธีอย่างมีประสิทธิภาพในโปรเจกต์ React ของคุณ โดยเฉพาะอย่างยิ่งเมื่อพัฒนาสำหรับผู้ใช้ทั่วโลก
ทำความเข้าใจ React Refs
Ref (ย่อมาจาก reference) เป็นฟีเจอร์ของ React ที่ช่วยให้คุณสามารถเข้าถึง DOM node หรือ React component ได้โดยตรง สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณต้องการ:
- จัดการ DOM element โดยตรง เช่น การโฟกัสช่อง input
- เข้าถึงเมธอดหรือคุณสมบัติของ child component
- จัดการค่าที่คงอยู่ตลอดการ re-render โดยไม่ทำให้เกิดการ re-render ซ้ำ (คล้ายกับ instance variables ใน class components)
แม้ว่า React จะสนับสนุนแนวทางแบบ declarative ซึ่ง UI จะถูกจัดการผ่าน state และ props แต่ก็มีบางสถานการณ์ที่จำเป็นต้องมีการจัดการโดยตรง Refs เป็นสะพานเชื่อมระหว่างธรรมชาติแบบ declarative ของ React กับการดำเนินการกับ DOM แบบ imperative
createRef
: แนวทางสำหรับ Class Component
createRef
เป็นเมธอดที่ React มีให้ใช้ โดยหลักแล้วจะใช้ภายใน class components เพื่อสร้าง Refs ทุกครั้งที่ class component ถูกสร้างขึ้นมา (instantiated) createRef
จะสร้างอ็อบเจกต์ Ref ใหม่ขึ้นมาเสมอ สิ่งนี้ช่วยให้แน่ใจว่าแต่ละ instance ของคอมโพเนนต์มี Ref ที่เป็นเอกลักษณ์ของตัวเอง
Syntax และการใช้งาน
ในการใช้ createRef
คุณจะต้องประกาศ Ref ใน class component ของคุณก่อน โดยทั่วไปจะทำใน constructor จากนั้นคุณก็นำ Ref ไปผูกกับ DOM element หรือคอมโพเนนต์โดยใช้ attribute ที่ชื่อว่า ref
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
// เข้าถึง DOM element หลังจากคอมโพเนนต์เมาท์แล้ว
this.myRef.current.focus();
}
render() {
return ;
}
}
ในตัวอย่างนี้ this.myRef
ถูกสร้างขึ้นโดยใช้ React.createRef()
จากนั้นจึงถูกกำหนดให้กับ attribute ref
ของ element input หลังจากที่คอมโพเนนต์เมาท์แล้ว (ใน componentDidMount
) คุณสามารถเข้าถึง DOM node จริงได้โดยใช้ this.myRef.current
และดำเนินการกับมันได้ (ในกรณีนี้คือการโฟกัสที่ input)
ตัวอย่าง: การโฟกัสช่อง Input
ลองพิจารณาสถานการณ์ที่คุณต้องการโฟกัสช่อง input โดยอัตโนมัติเมื่อคอมโพเนนต์เมาท์ นี่เป็นกรณีการใช้งานทั่วไปสำหรับ Refs โดยเฉพาะในฟอร์มหรือองค์ประกอบที่มีการโต้ตอบ
class FocusInput extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
);
}
}
ในตัวอย่างนี้ FocusInput
จะโฟกัสที่ช่อง input ทันทีหลังจากที่เมาท์แล้ว ซึ่งสามารถปรับปรุงประสบการณ์ของผู้ใช้โดยการดึงดูดความสนใจของผู้ใช้ไปยัง element input ทันทีที่คอมโพเนนต์ถูกเรนเดอร์
ข้อควรพิจารณาที่สำคัญเกี่ยวกับ createRef
- สำหรับ Class Components เท่านั้น:
createRef
ถูกออกแบบมาเพื่อใช้ใน class components แม้ว่าในทางเทคนิคอาจทำงานใน functional components ได้ แต่ก็ไม่ใช่การใช้งานที่ตั้งใจไว้และอาจนำไปสู่พฤติกรรมที่ไม่คาดคิดได้ - Ref ใหม่ในทุก Instance: แต่ละ instance ของ class component จะได้รับ
createRef
ของตัวเอง ซึ่งสำคัญต่อการรักษาความเป็นอิสระระหว่าง instance ของคอมโพเนนต์
useRef
: Hook สำหรับ Functional Component
useRef
เป็น Hook ที่ถูกนำมาใช้ใน React 16.8 มันเป็นวิธีในการสร้างอ็อบเจกต์ Ref ที่เปลี่ยนแปลงค่าได้ (mutable) ภายใน functional components ซึ่งแตกต่างจาก createRef
ตรงที่ useRef
จะคืนค่าอ็อบเจกต์ Ref เดิมทุกครั้งที่คอมโพเนนต์เรนเดอร์ ทำให้เหมาะอย่างยิ่งสำหรับการเก็บค่าไว้ตลอดการ re-render โดยไม่ทำให้เกิดการ re-render ซ้ำ
Syntax และการใช้งาน
การใช้ useRef
นั้นตรงไปตรงมา คุณเรียกใช้ useRef
Hook โดยส่งค่าเริ่มต้นเข้าไป Hook จะคืนค่าอ็อบเจกต์ที่มี property .current
ซึ่งคุณสามารถใช้เพื่อเข้าถึงและแก้ไขค่าได้
import React, { useRef, useEffect } from 'react';
function MyFunctionalComponent() {
const myRef = useRef(null);
useEffect(() => {
// เข้าถึง DOM element หลังจากคอมโพเนนต์เมาท์แล้ว
if (myRef.current) {
myRef.current.focus();
}
}, []);
return ;
}
ในตัวอย่างนี้ useRef(null)
สร้าง Ref ที่มีค่าเริ่มต้นเป็น null
useEffect
Hook ถูกใช้เพื่อเข้าถึง DOM element หลังจากที่คอมโพเนนต์เมาท์แล้ว property myRef.current
จะเก็บการอ้างอิงไปยัง element input ทำให้คุณสามารถโฟกัสได้
ตัวอย่าง: การติดตามค่า Prop ก่อนหน้า
หนึ่งในกรณีการใช้งานที่ทรงพลังของ useRef
คือการติดตามค่าก่อนหน้าของ prop เนื่องจากการเปลี่ยนแปลงค่าใน Refs ไม่ได้ทำให้เกิดการ re-render คุณจึงสามารถใช้มันเพื่อเก็บค่าที่คุณต้องการให้คงอยู่ตลอดการ re-render โดยไม่ส่งผลกระทบต่อ UI
import React, { useRef, useEffect } from 'react';
function PreviousValueComponent({ value }) {
const previousValue = useRef();
useEffect(() => {
previousValue.current = value;
}, [value]);
return (
Current Value: {value}
Previous Value: {previousValue.current}
);
}
ในตัวอย่างนี้ previousValue.current
จะเก็บค่าก่อนหน้าของ prop ที่ชื่อ value
useEffect
Hook จะอัปเดต Ref ทุกครั้งที่ prop value
เปลี่ยนแปลง ซึ่งช่วยให้คุณสามารถเปรียบเทียบค่าปัจจุบันและค่าก่อนหน้าได้ ซึ่งมีประโยชน์สำหรับการตรวจจับการเปลี่ยนแปลงหรือการสร้างแอนิเมชัน
ข้อควรพิจารณาที่สำคัญเกี่ยวกับ useRef
- สำหรับ Functional Components เท่านั้น:
useRef
เป็น Hook และสามารถใช้ได้เฉพาะภายใน functional components หรือ custom Hooks เท่านั้น - คงอยู่ตลอดการ Renders:
useRef
Hook จะคืนค่าอ็อบเจกต์ Ref เดิมในทุกๆ การเรนเดอร์ นี่คือกุญแจสำคัญของความสามารถในการเก็บค่าไว้โดยไม่ทำให้เกิดการ re-render ซ้ำ - Property
.current
ที่เปลี่ยนแปลงได้: คุณสามารถแก้ไข property.current
ของอ็อบเจกต์ Ref ได้โดยตรง - ค่าเริ่มต้น: คุณสามารถกำหนดค่าเริ่มต้นให้กับ
useRef
ได้ ค่านั้นจะถูกกำหนดให้กับ property.current
เมื่อคอมโพเนนต์ถูกเรนเดอร์ครั้งแรก - ไม่ทำให้เกิด Re-renders: การแก้ไข property
.current
ของ Ref ไม่ได้ทำให้คอมโพเนนต์ re-render
useRef
vs. createRef
: การเปรียบเทียบโดยละเอียด
หลังจากที่เราได้สำรวจทั้ง useRef
และ createRef
แยกกันแล้ว มาเปรียบเทียบกันแบบตัวต่อตัวเพื่อเน้นย้ำถึงความแตกต่างที่สำคัญและเมื่อใดควรเลือกใช้อันไหน
คุณสมบัติ | useRef |
createRef |
---|---|---|
ประเภทคอมโพเนนต์ | Functional Components | Class Components |
Hook หรือ Method | Hook | Method |
Ref Instance | คืนค่าอ็อบเจกต์ Ref เดิมในทุกการเรนเดอร์ | สร้างอ็อบเจกต์ Ref ใหม่ในทุก instance ของคอมโพเนนต์ |
กรณีการใช้งาน |
|
|
การเลือก Ref ที่เหมาะสม: คู่มือการตัดสินใจ
นี่คือคู่มือง่ายๆ ที่จะช่วยให้คุณเลือกระหว่าง useRef
และ createRef
:
- คุณกำลังทำงานกับ functional component หรือไม่? ใช้
useRef
- คุณกำลังทำงานกับ class component หรือไม่? ใช้
createRef
- คุณต้องการเก็บค่าไว้ตลอดการ re-render โดยไม่ทำให้เกิดการ re-render ซ้ำหรือไม่? ใช้
useRef
- คุณต้องการติดตามค่าก่อนหน้าของ prop หรือไม่? ใช้
useRef
นอกเหนือจากการจัดการ DOM: กรณีการใช้งานขั้นสูงสำหรับ Refs
ในขณะที่การเข้าถึงและจัดการ DOM elements เป็นกรณีการใช้งานหลักของ Refs แต่พวกมันยังมีความสามารถที่นอกเหนือไปจากฟังก์ชันหลักนี้ มาสำรวจสถานการณ์ขั้นสูงบางอย่างที่ Refs สามารถมีประโยชน์เป็นพิเศษได้
1. การเข้าถึงเมธอดของ Child Component
Refs สามารถใช้เพื่อเข้าถึงเมธอดที่กำหนดไว้ใน child components ได้ ซึ่งช่วยให้ parent component สามารถสั่งการทำงานหรือดึงข้อมูลจาก children ได้โดยตรง แนวทางนี้มีประโยชน์อย่างยิ่งเมื่อคุณต้องการควบคุม child components อย่างละเอียด
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
}
handleClick = () => {
// เรียกใช้เมธอดบน child component
this.childRef.current.doSomething();
};
render() {
return (
);
}
}
class ChildComponent extends React.Component {
doSomething = () => {
console.log('Child component action triggered!');
};
render() {
return This is a child component.;
}
}
ในตัวอย่างนี้ ParentComponent
ใช้ Ref เพื่อเข้าถึง ChildComponent
และเรียกใช้เมธอด doSomething
ของมัน
2. การจัดการโฟกัสและการเลือก
Refs มีค่าอย่างยิ่งสำหรับการจัดการโฟกัสและการเลือกภายในช่อง input และองค์ประกอบเชิงโต้ตอบอื่นๆ นี่เป็นสิ่งสำคัญสำหรับการสร้างอินเทอร์เฟซที่เข้าถึงได้และเป็นมิตรต่อผู้ใช้
import React, { useRef, useEffect } from 'react';
function FocusOnMount() {
const inputRef = useRef(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
inputRef.current.select(); // เลือกข้อความใน input
}
}, []);
return ;
}
ตัวอย่างนี้จะโฟกัสที่ input และเลือกข้อความทันทีที่คอมโพเนนต์เมาท์
3. การสร้างแอนิเมชันให้กับ Elements
Refs สามารถใช้ร่วมกับไลบรารีแอนิเมชัน (เช่น GreenSock หรือ Framer Motion) เพื่อจัดการ DOM โดยตรงและสร้างแอนิเมชันที่ซับซ้อน ซึ่งช่วยให้สามารถควบคุมลำดับของแอนิเมชันได้อย่างละเอียด
ตัวอย่างโดยใช้ JavaScript พื้นฐานเพื่อความเรียบง่าย:
import React, { useRef, useEffect } from 'react';
function AnimatedBox() {
const boxRef = useRef(null);
useEffect(() => {
const box = boxRef.current;
if (box) {
// แอนิเมชันง่ายๆ: ย้ายกล่องไปทางขวา
box.animate(
[
{ transform: 'translateX(0)' },
{ transform: 'translateX(100px)' },
],
{
duration: 1000, // 1 วินาที
iterations: Infinity, // เล่นซ้ำตลอดไป
direction: 'alternate',
}
);
}
}, []);
return ;
}
ตัวอย่างนี้ใช้ Web Animations API เพื่อสร้างแอนิเมชันให้กับกล่องธรรมดา โดยย้ายไปมาในแนวนอน
แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้ React Refs ในแอปพลิเคชันระดับโลก
เมื่อพัฒนาแอปพลิเคชัน React สำหรับผู้ใช้ทั่วโลก สิ่งสำคัญคือต้องพิจารณาว่า Refs มีปฏิสัมพันธ์กับการทำ Internationalization (i18n) และ Localization (l10n) อย่างไร นี่คือแนวทางปฏิบัติที่ดีที่สุดบางส่วน:
1. การเข้าถึงได้ (Accessibility - A11y)
ตรวจสอบให้แน่ใจว่าการใช้ Refs ของคุณไม่ส่งผลเสียต่อการเข้าถึงได้ ตัวอย่างเช่น เมื่อทำการโฟกัส elements ด้วยโปรแกรม ให้พิจารณาลำดับการโฟกัสของผู้ใช้ และพิจารณาว่าการเปลี่ยนแปลงโฟกัสนั้นเหมาะสมสำหรับโปรแกรมอ่านหน้าจอและผู้ใช้คีย์บอร์ดหรือไม่
import React, { useRef, useEffect } from 'react';
function AccessibleFocus() {
const buttonRef = useRef(null);
useEffect(() => {
const button = buttonRef.current;
if (button) {
// โฟกัสเฉพาะเมื่อผู้ใช้ยังไม่ได้โฟกัสที่ปุ่ม
if (document.activeElement !== button) {
button.focus();
}
}
}, []);
return ;
}
2. ช่อง Input ที่รองรับหลายภาษา (Internationalized Input Fields)
เมื่อทำงานกับช่อง input โปรดระวังวิธีการป้อนข้อมูลและชุดอักขระที่แตกต่างกันในแต่ละภาษา ตรวจสอบให้แน่ใจว่าการจัดการผ่าน Ref ของคุณ (เช่น การเลือก, ตำแหน่งเคอร์เซอร์) ทำงานได้อย่างถูกต้องกับประเภทการป้อนข้อมูลและภาษาต่างๆ ทดสอบคอมโพเนนต์ของคุณอย่างละเอียดกับภาษาและวิธีการป้อนข้อมูลที่แตกต่างกัน
3. เลย์เอาต์แบบขวาไปซ้าย (Right-to-Left - RTL)
หากแอปพลิเคชันของคุณรองรับภาษา RTL (เช่น ภาษาอาหรับ, ฮีบรู) ตรวจสอบให้แน่ใจว่าการจัดการ DOM ของคุณโดยใช้ Refs คำนึงถึงเลย์เอาต์ที่กลับด้านด้วย ตัวอย่างเช่น เมื่อสร้างแอนิเมชันให้กับ elements ให้พิจารณาการกลับทิศทางของแอนิเมชันสำหรับภาษา RTL
4. ข้อควรพิจารณาด้านประสิทธิภาพ
แม้ว่า Refs จะเป็นวิธีที่ทรงพลังในการโต้ตอบกับ DOM แต่การใช้งานมากเกินไปอาจนำไปสู่ปัญหาด้านประสิทธิภาพได้ การจัดการ DOM โดยตรงจะข้ามกระบวนการ virtual DOM และ reconciliation ของ React ซึ่งอาจนำไปสู่ความไม่สอดคล้องกันและการอัปเดตที่ช้าลง ใช้ Refs อย่างรอบคอบและเฉพาะเมื่อจำเป็นเท่านั้น
สรุป
React Refs โดยเฉพาะ useRef
และ createRef
เป็นเครื่องมือที่จำเป็นสำหรับนักพัฒนา React การทำความเข้าใจความแตกต่างของแต่ละวิธีและเวลาที่จะนำไปใช้อย่างมีประสิทธิภาพเป็นสิ่งสำคัญสำหรับการสร้างแอปพลิเคชันที่แข็งแกร่งและมีประสิทธิภาพ createRef
ยังคงเป็นมาตรฐานสำหรับการจัดการ Refs ภายใน class components เพื่อให้แน่ใจว่าแต่ละ instance มี Ref ที่เป็นเอกลักษณ์ของตัวเอง ส่วน useRef
ด้วยธรรมชาติที่คงอยู่ตลอดการ re-render จึงเหมาะสำหรับ functional components โดยเป็นวิธีในการจัดการ DOM elements และเก็บค่าไว้โดยไม่ทำให้เกิดการ re-render ที่ไม่จำเป็น ด้วยการใช้เครื่องมือเหล่านี้อย่างชาญฉลาด คุณสามารถเพิ่มฟังก์ชันการทำงานและประสบการณ์ผู้ใช้ของแอปพลิเคชัน React ของคุณ เพื่อตอบสนองผู้ใช้ทั่วโลกด้วยอินเทอร์เฟซที่เข้าถึงได้และมีประสิทธิภาพ
ในขณะที่ React ยังคงพัฒนาต่อไป การเชี่ยวชาญแนวคิดพื้นฐานเหล่านี้จะช่วยให้คุณสามารถสร้างประสบการณ์เว็บที่เป็นนวัตกรรมและเป็นมิตรต่อผู้ใช้ซึ่งก้าวข้ามขอบเขตทางภูมิศาสตร์และวัฒนธรรม อย่าลืมให้ความสำคัญกับการเข้าถึงได้ (accessibility), การรองรับหลายภาษา (internationalization) และประสิทธิภาพ (performance) เพื่อส่งมอบแอปพลิเคชันระดับโลกอย่างแท้จริง