สำรวจเครื่องมือการเรนเดอร์ DOM อันทรงพลังของ React ReactDOM เรียนรู้เกี่ยวกับ ReactDOM.render, hydrate, unmountComponentAtNode และ findDOMNode เพื่อสร้าง UI แบบไดนามิก
React ReactDOM: คู่มือฉบับสมบูรณ์เกี่ยวกับเครื่องมือสำหรับการเรนเดอร์ DOM
React เป็นไลบรารี JavaScript ที่ทรงพลังสำหรับสร้าง User Interface (UI) โดยหัวใจหลักของ React คือการลดความซับซ้อนในการจัดการ Document Object Model (DOM) โดยตรง ทำให้นักพัฒนาสามารถมุ่งเน้นไปที่การอธิบายสถานะที่ต้องการของ UI ได้ อย่างไรก็ตาม React เองก็ต้องการวิธีในการโต้ตอบกับ DOM ของเบราว์เซอร์เพื่อทำให้ UI ที่ออกแบบไว้มีชีวิตขึ้นมา นี่คือจุดที่ ReactDOM เข้ามามีบทบาท แพ็กเกจนี้มีเมธอดเฉพาะสำหรับการเรนเดอร์คอมโพเนนต์ React ลงใน DOM และจัดการการโต้ตอบระหว่างกัน
ทำความเข้าใจบทบาทของ ReactDOM
ReactDOM ทำหน้าที่เป็นสะพานเชื่อมระหว่างโลกของคอมโพเนนต์ใน React กับ DOM ของเบราว์เซอร์ โดยมีความสามารถในการเรนเดอร์คอมโพเนนต์ React ไปยัง DOM node ที่ต้องการ, อัปเดตเมื่อข้อมูลเปลี่ยนแปลง และแม้กระทั่งลบออกเมื่อไม่ต้องการใช้งานอีกต่อไป ลองนึกภาพว่ามันเป็นเหมือนเครื่องยนต์ที่ขับเคลื่อนการแสดงผลของแอปพลิเคชัน React ของคุณในเบราว์เซอร์
สิ่งสำคัญคือต้องแยกความแตกต่างระหว่าง React และ ReactDOM โดย React เป็นไลบรารีหลักสำหรับสร้างคอมโพเนนต์และจัดการ state ส่วน ReactDOM รับผิดชอบในการนำคอมโพเนนต์เหล่านั้นไปเรนเดอร์ลงใน DOM ของเบราว์เซอร์ แม้ว่า React จะสามารถใช้ในสภาพแวดล้อมอื่นได้ (เช่น React Native สำหรับการพัฒนาแอปพลิเคชันมือถือ ซึ่งใช้ไลบรารีการเรนเดอร์ที่แตกต่างกัน) แต่ ReactDOM ถูกออกแบบมาโดยเฉพาะสำหรับเว็บแอปพลิเคชัน
เมธอดหลักของ ReactDOM
เรามาสำรวจเมธอดที่สำคัญที่สุดบางส่วนที่ ReactDOM มีให้:
ReactDOM.render()
เมธอด ReactDOM.render()
เป็นรากฐานของแอปพลิเคชัน React ทุกตัว มีหน้าที่ในการเมานต์ (mount) คอมโพเนนต์ React (หรือโครงสร้างของคอมโพเนนต์) เข้าไปยัง DOM node ที่ระบุ โดย node นี้มักจะเป็น HTML element ว่างๆ ภายในหน้าเว็บของคุณ
รูปแบบการใช้งาน:
ReactDOM.render(element, container[, callback])
element
: React element ที่คุณต้องการเรนเดอร์ โดยปกติจะเป็นคอมโพเนนต์ระดับบนสุดของแอปพลิเคชันของคุณcontainer
: DOM element ที่คุณต้องการเมานต์คอมโพเนนต์ ซึ่งควรเป็น DOM node ที่ถูกต้องใน HTML ของคุณcallback
(ทางเลือก): ฟังก์ชันที่จะถูกเรียกใช้งานหลังจากคอมโพเนนต์ถูกเรนเดอร์เสร็จสิ้น
ตัวอย่าง:
สมมติว่าคุณมีคอมโพเนนต์ React ง่ายๆ ที่ชื่อว่า App
:
import React from 'react';
import ReactDOM from 'react-dom/client';
function App() {
return (
<div>
<h1>Hello, React!</h1>
<p>This is a simple React component.</p>
</div>
);
}
และไฟล์ HTML ที่มี element ที่มี ID ว่า "root":
<div id="root"></div>
ในการเรนเดอร์คอมโพเนนต์ App
เข้าไปใน element "root" คุณจะใช้โค้ดดังนี้:
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
ข้อควรทราบ (React 18 และใหม่กว่า): ใน React 18 ขึ้นไป ReactDOM.render
ถือว่าเป็น legacy วิธีที่แนะนำคือการใช้ ReactDOM.createRoot
ดังที่แสดงในตัวอย่างข้างต้น ซึ่งจะเปิดใช้งานฟีเจอร์ concurrent ใหม่ที่มาพร้อมกับ React 18
การทำความเข้าใจเรื่องการอัปเดต: ReactDOM.render()
ยังรับผิดชอบในการอัปเดต DOM เมื่อข้อมูลของคอมโพเนนต์เปลี่ยนแปลง React ใช้ virtual DOM เพื่อเปรียบเทียบสถานะปัจจุบันกับสถานะที่ต้องการอย่างมีประสิทธิภาพ และจะอัปเดตเฉพาะส่วนที่จำเป็นของ DOM จริงเท่านั้น ซึ่งช่วยลดภาระด้านประสิทธิภาพ
ReactDOM.hydrate()
ReactDOM.hydrate()
ใช้เมื่อคุณกำลังเรนเดอร์แอปพลิเคชัน React ที่ถูกเรนเดอร์บนเซิร์ฟเวอร์มาแล้ว นี่เป็นเทคนิคสำคัญในการปรับปรุงประสิทธิภาพการโหลดเริ่มต้นของแอปพลิเคชันและเพิ่มประสิทธิภาพ SEO
Server-Side Rendering (SSR): ใน SSR คอมโพเนนต์ React จะถูกเรนเดอร์เป็น HTML บนเซิร์ฟเวอร์ จากนั้น HTML นี้จะถูกส่งไปยังเบราว์เซอร์ ซึ่งสามารถแสดงเนื้อหาเริ่มต้นได้ทันที อย่างไรก็ตาม เบราว์เซอร์ยังคงต้อง "hydrate" แอปพลิเคชัน นั่นคือการแนบ event listeners และทำให้แอปพลิเคชันสามารถโต้ตอบได้ ReactDOM.hydrate()
จะนำ HTML ที่เรนเดอร์จากเซิร์ฟเวอร์มาแนบ React event handlers เข้าไป ทำให้แอปพลิเคชันทำงานได้อย่างสมบูรณ์
รูปแบบการใช้งาน:
ReactDOM.hydrate(element, container[, callback])
พารามิเตอร์เหมือนกับ ReactDOM.render()
ตัวอย่าง:
บนเซิร์ฟเวอร์ คุณจะเรนเดอร์แอปพลิเคชัน React ของคุณเป็นสตริง:
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';
const html = ReactDOMServer.renderToString(<App />);
จากนั้น HTML นี้จะถูกส่งไปยังฝั่ง client
ทางฝั่ง client คุณจะใช้ ReactDOM.hydrate()
เพื่อแนบ React event handlers:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.hydrate(<App />);
ประโยชน์ของ Hydration:
- ปรับปรุงเวลาในการโหลดครั้งแรก: ผู้ใช้เห็นเนื้อหาทันที แม้ว่าโค้ด JavaScript จะยังโหลดไม่เสร็จสมบูรณ์
- ปรับปรุง SEO: Search engines สามารถเข้ามาอ่านและจัดทำดัชนี HTML ที่เรนเดอร์เสร็จสมบูรณ์ได้
ReactDOM.unmountComponentAtNode()
ReactDOM.unmountComponentAtNode()
ใช้เพื่อลบคอมโพเนนต์ที่เมานต์แล้วออกจาก DOM ซึ่งมีประโยชน์เมื่อคุณต้องการลบส่วนต่างๆ ของ UI แบบไดนามิก หรือเมื่อคุณกำลังล้างทรัพยากรก่อนที่จะไปยังหน้าอื่น
รูปแบบการใช้งาน:
ReactDOM.unmountComponentAtNode(container)
container
: DOM element ที่คอมโพเนนต์ถูกเมานต์อยู่
ตัวอย่าง:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
// ต่อมา เพื่อที่จะ unmount คอมโพเนนต์:
root.unmount();
หลังจากเรียก ReactDOM.unmountComponentAtNode(rootElement)
คอมโพเนนต์ App
จะถูกลบออกจาก DOM และ event listeners ทั้งหมดรวมถึงทรัพยากรที่เกี่ยวข้องจะถูกล้างออกไป
ควรใช้เมื่อใด:
- การลบ modal หรือ dialog ออกจาก UI
- การล้างข้อมูลของคอมโพเนนต์ก่อนที่จะไปยังหน้าอื่น
- การสลับระหว่างคอมโพเนนต์ต่างๆ แบบไดนามิก
ReactDOM.findDOMNode() (Legacy)
สำคัญ: ReactDOM.findDOMNode()
ถือว่าเป็น legacy และไม่แนะนำให้ใช้ในแอปพลิเคชัน React สมัยใหม่ ในอดีตเคยใช้เพื่อเข้าถึง DOM node ที่อยู่เบื้องหลังของคอมโพเนนต์ที่เมานต์แล้ว อย่างไรก็ตาม การใช้งานนี้ไม่ได้รับการสนับสนุนเนื่องจากเป็นการทำลาย abstraction ของ React และอาจนำไปสู่พฤติกรรมที่คาดเดาไม่ได้ โดยเฉพาะอย่างยิ่งหลังจากการมาถึงของ functional components และ hooks
แนวทางทางเลือก:
แทนที่จะใช้ ReactDOM.findDOMNode()
ให้พิจารณาแนวทางทางเลือกเหล่านี้:
- Refs: ใช้ React refs เพื่อเข้าถึง DOM nodes โดยตรง นี่เป็นวิธีที่แนะนำสำหรับการโต้ตอบกับ DOM elements
- Controlled Components: ทำให้คอมโพเนนต์ของคุณเป็น "controlled" โดยการจัดการ state ของมันด้วย React ซึ่งช่วยให้คุณสามารถจัดการ UI ได้โดยไม่ต้องเข้าถึง DOM โดยตรง
- Event Handlers: แนบ event handlers เข้ากับคอมโพเนนต์ของคุณและใช้ event object เพื่อเข้าถึง DOM element เป้าหมาย
Concurrency ใน React 18 และ ReactDOM
React 18 ได้นำเสนอ concurrency ซึ่งเป็นกลไกใหม่ที่ช่วยให้ React สามารถขัดจังหวะ, หยุดชั่วคราว, ทำต่อ หรือยกเลิกงานเรนเดอร์ได้ สิ่งนี้ปลดล็อกฟีเจอร์ที่ทรงพลังอย่าง transitions และ selective hydration ซึ่งนำไปสู่ประสบการณ์ผู้ใช้ที่ราบรื่นและตอบสนองได้ดียิ่งขึ้น
ผลกระทบต่อ ReactDOM: การปรับใช้ ReactDOM.createRoot
เป็นสิ่งสำคัญอย่างยิ่งในการใช้ประโยชน์จาก concurrency เมธอดนี้จะสร้าง root ที่แอปพลิเคชันของคุณจะถูกเรนเดอร์ ซึ่งช่วยให้ React สามารถจัดการงานเรนเดอร์ได้อย่างมีประสิทธิภาพมากขึ้น
Transitions: Transitions ช่วยให้คุณสามารถทำเครื่องหมายการอัปเดต state บางอย่างว่าไม่เร่งด่วน ทำให้ React สามารถจัดลำดับความสำคัญของการอัปเดตที่สำคัญกว่าและรักษาการตอบสนองได้ ตัวอย่างเช่น เมื่อเปลี่ยนเส้นทาง (route) คุณสามารถทำเครื่องหมายการเปลี่ยนเส้นทางว่าเป็นการอัปเดตที่ไม่เร่งด่วน เพื่อให้แน่ใจว่า UI ยังคงตอบสนองได้แม้ในระหว่างการดึงข้อมูล
Selective Hydration: ด้วย selective hydration, React สามารถ hydrate คอมโพเนนต์แต่ละตัวตามความต้องการ แทนที่จะต้อง hydrate ทั้งแอปพลิเคชันในคราวเดียว ซึ่งช่วยปรับปรุงเวลาในการโหลดเริ่มต้นสำหรับแอปพลิเคชันขนาดใหญ่ได้อย่างมีนัยสำคัญ
ข้อควรพิจารณาสำหรับ React ReactDOM ในระดับสากล
เมื่อพัฒนาแอปพลิเคชัน React สำหรับผู้ใช้ทั่วโลก สิ่งสำคัญคือต้องพิจารณาปัจจัยต่างๆ เช่น internationalization (i18n) และ localization (l10n) ตัว ReactDOM เองไม่ได้จัดการด้านเหล่านี้โดยตรง แต่การผสานรวมกับไลบรารี i18n และแนวทางปฏิบัติที่ดีที่สุดเป็นสิ่งสำคัญ
- Internationalization (i18n): กระบวนการออกแบบและพัฒนาแอปพลิเคชันที่สามารถปรับให้เข้ากับภาษาและภูมิภาคต่างๆ ได้โดยไม่จำเป็นต้องมีการเปลี่ยนแปลงทางวิศวกรรม
- Localization (l10n): กระบวนการปรับแอปพลิเคชันที่ทำ internationalization แล้วให้เข้ากับภาษาหรือภูมิภาคที่เฉพาะเจาะจง โดยการแปลข้อความ, ปรับรูปแบบ และจัดการกับความแตกต่างทางวัฒนธรรม
การใช้ไลบรารี i18n:
ไลบรารีอย่าง react-i18next
และ globalize
มีเครื่องมือสำหรับจัดการคำแปล, การจัดรูปแบบวันที่และเวลา และงานอื่นๆ ที่เกี่ยวข้องกับ localization ไลบรารีเหล่านี้มักจะทำงานร่วมกับ React และ ReactDOM ได้อย่างราบรื่น
ตัวอย่างการใช้ react-i18next:
import React from 'react';
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t } = useTranslation();
return (
<div>
<h1>{t('greeting')}</h1>
<p>{t('description')}</p>
</div>
);
}
ในตัวอย่างนี้ hook useTranslation
ให้การเข้าถึงฟังก์ชันการแปล t
ซึ่งจะดึงคำแปลที่เหมาะสมสำหรับ key ที่กำหนด โดยคำแปลต่างๆ มักจะถูกเก็บไว้ในไฟล์แยกต่างหากสำหรับแต่ละภาษา
การจัดวางแบบขวาไปซ้าย (RTL):
บางภาษา เช่น ภาษาอาหรับและฮีบรู เขียนจากขวาไปซ้าย เมื่อพัฒนาแอปพลิเคชันสำหรับภาษาเหล่านี้ คุณต้องแน่ใจว่า UI ของคุณรองรับการจัดวางแบบ RTL ซึ่งโดยทั่วไปจะเกี่ยวข้องกับการปรับทิศทางของข้อความ, การสะท้อนการจัดวางของคอมโพเนนต์ และการจัดการข้อความสองทิศทาง
แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้ ReactDOM
เพื่อให้แน่ใจว่าแอปพลิเคชัน React มีประสิทธิภาพและบำรุงรักษาง่าย ให้ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้เมื่อใช้ ReactDOM:
- ใช้
ReactDOM.createRoot
ใน React 18 และใหม่กว่า: นี่เป็นวิธีที่แนะนำในการเรนเดอร์แอปพลิเคชันของคุณและใช้ประโยชน์จาก concurrency - หลีกเลี่ยงการจัดการ DOM โดยตรง: ให้ React จัดการ DOM การจัดการ DOM โดยตรงอาจนำไปสู่ความไม่สอดคล้องและปัญหาด้านประสิทธิภาพ
- ใช้ refs เท่าที่จำเป็น: ใช้ refs เฉพาะเมื่อคุณต้องการเข้าถึง DOM nodes โดยตรงเพื่อวัตถุประสงค์เฉพาะ เช่น การโฟกัสที่ input element
- เพิ่มประสิทธิภาพการเรนเดอร์: ใช้เทคนิคต่างๆ เช่น memoization และ shouldComponentUpdate เพื่อป้องกันการ re-render ที่ไม่จำเป็น
- พิจารณาการทำ server-side rendering เพื่อปรับปรุงประสิทธิภาพและ SEO
- ใช้ไลบรารี i18n สำหรับ internationalization และ localization
- ทดสอบแอปพลิเคชันของคุณอย่างละเอียดในเบราว์เซอร์และอุปกรณ์ต่างๆ
สรุป
ReactDOM เป็นส่วนสำคัญของระบบนิเวศ React โดยทำหน้าที่เป็นสะพานเชื่อมระหว่างคอมโพเนนต์ React กับ DOM ของเบราว์เซอร์ ด้วยการทำความเข้าใจเมธอดหลักต่างๆ เช่น ReactDOM.render()
, ReactDOM.hydrate()
และ ReactDOM.unmountComponentAtNode()
และการนำแนวทางปฏิบัติที่ดีที่สุดมาใช้ คุณจะสามารถสร้างแอปพลิเคชัน React ที่มีประสิทธิภาพ, บำรุงรักษาง่าย และเข้าถึงได้ทั่วโลก ด้วยการมาถึงของ concurrency ใน React 18 การใช้ ReactDOM.createRoot
จึงเป็นสิ่งสำคัญในการปลดล็อกประสิทธิภาพและการตอบสนองในระดับใหม่ อย่าลืมพิจารณาแนวทางปฏิบัติที่ดีที่สุดด้าน internationalization และ localization เมื่อสร้างแอปสำหรับผู้ใช้ทั่วโลก เพื่อสร้างประสบการณ์ผู้ใช้ที่ครอบคลุมและเข้าถึงได้อย่างแท้จริง