Refs, Portals & Escape Hatches
เมื่อ declarative ไม่พอ
หัวข้อที่มีชื่อว่า “เมื่อ declarative ไม่พอ”99% ของ React เป็น declarative: เปลี่ยน state, describe UI, ให้ React sync DOM แต่ปัญหาจริงบางอย่างต้องให้คุณก้าว ออกนอก model นั้น — เอื้อมเข้าไปใน DOM, บังคับ timing หรือ render ไปยังที่ที่ component tree ไม่เปิดให้โดยธรรมชาติ React ให้ escape hatch ที่ตั้งใจไว้สำหรับสิ่งเหล่านี้ ทักษะคือรู้ว่ามีอยู่และใช้อย่างประหยัด
Portals: render ออกนอก DOM ของ parent
หัวข้อที่มีชื่อว่า “Portals: render ออกนอก DOM ของ parent”portal render children ไปยังส่วน อื่น ของ DOM tree โดยที่ยังคงอยู่ใน React tree ตรงที่คุณเขียน นี่คือคำตอบมาตรฐานสำหรับ modal, tooltip และ dropdown — UI ที่ต้องหนี overflow: hidden หรือ stacking context ของ z-index ของ parent
import { createPortal } from "react-dom";
function Modal({ children }) { // render เข้า document.body แต่ยังเป็น React child ของที่ที่ <Modal/> ถูกใช้ return createPortal( <div className="overlay">{children}</div>, document.body, );}flowchart TB react["React tree: App -> Page -> Modal -> content"] dom["DOM tree: body -> app-root ... body -> overlay (portal target)"] react -->|ทางตรรกะเป็น child ของ Page| dom note["event ยัง bubble ผ่าน React tree ไม่ใช่ตำแหน่ง DOM"]
จุดละเอียดสำคัญ: portal เปลี่ยน ที่ที่ DOM node อยู่ ไม่ใช่ ที่ที่อยู่ใน React tree context ยังไหลเข้า และ event ยัง bubble ขึ้นผ่าน React parent — ดังนั้นการคลิกภายใน modal ที่ portal ยังไปถึง handler บน React ancestor เชิงตรรกะของตัวเอง
Imperative ref: เรียกเข้า child หรือ DOM
หัวข้อที่มีชื่อว่า “Imperative ref: เรียกเข้า child หรือ DOM”ส่วนใหญ่ data ไหลลงผ่าน props แต่บางครั้ง parent ต้อง imperatively สั่งอะไรบางอย่างบน child หรือ DOM node — focus input, เล่น video, scroll ไปยัง element ref ให้ handle นั้นกับคุณ
function SearchForm() { const inputRef = useRef(null); return ( <> <input ref={inputRef} /> <button onClick={() => inputRef.current.focus()}>Focus</button> </> );}ถ้าจะเปิดเผย imperative API แบบกำหนดเอง จาก component (แทน DOM node ดิบ) ใช้ useImperativeHandle กำหนดว่า ref ของ parent เรียก method อะไรได้บ้าง เลือกใช้ props/state สำหรับสิ่งที่เป็น declarative ได้ และหยิบ imperative ref มาเฉพาะ action ที่เป็น imperative จริง (focus, scroll, media, การวัดขนาด)
flushSync: ออกจาก batching
หัวข้อที่มีชื่อว่า “flushSync: ออกจาก batching”React batch การ update state และ apply DOM change แบบ asynchronous เพื่อ performance บางครั้งคุณต้องการให้ DOM ถูก update แบบ synchronous — เช่นเพื่ออ่าน layout ใหม่ทันที (วัด element ที่เพิ่งเพิ่ม แล้ว scroll ไปหา) flushSync บังคับให้ state update และการ commit DOM ของตัวเองเกิดขึ้นทันที
import { flushSync } from "react-dom";
flushSync(() => setItems([...items, newItem])); // DOM ถูก update แล้วเมื่อบรรทัดนี้ returnlistRef.current.scrollTop = listRef.current.scrollHeight; // ตอนนี้อ่าน/scroll ได้ปลอดภัยflushSync ทำลาย batching และทำ performance แย่ลง ดังนั้นใช้เฉพาะเมื่อจำเป็นจริง ๆ ที่ต้องอ่านหรือทำอะไรกับ DOM ก่อน browser paint