Transitions
ปัญหา: update ที่ช้า block อันที่เร็ว
หัวข้อที่มีชื่อว่า “ปัญหา: update ที่ช้า block อันที่เร็ว”ลองนึกถึง search box ที่ filter list ยักษ์ ทุก keystroke อัปเดตสองอย่าง: ค่าใน input (ต้องรู้สึกทันที) และผลลัพธ์ที่ filter แล้ว (คำนวณแพง) ถ้าทั้งคู่ urgent การ filter ที่แพงจะ block keystroke และพิมพ์จะสะดุด
Transitions ให้คุณแยกสองอย่างนี้ออก: keystroke ยัง urgent ส่วนการ filter กลายเป็น transition priority ต่ำที่ React interrupt ได้เมื่อ keystroke ถัดไปมา
useTransition: mark update เป็น non-urgent
หัวข้อที่มีชื่อว่า “useTransition: mark update เป็น non-urgent”useTransition คืน [isPending, startTransition] ครอบ state update ที่เป็น non-urgent ไว้ใน startTransition และใช้ isPending แสดง hint “updating” เบา ๆ
function SearchResults({ allItems }) { const [query, setQuery] = useState(""); const [results, setResults] = useState(allItems); const [isPending, startTransition] = useTransition();
function handleChange(e) { setQuery(e.target.value); // urgent — input อัปเดตทันที startTransition(() => { // non-urgent — React render อันนี้เบื้องหลัง, interrupt ได้ setResults(filterExpensive(allItems, e.target.value)); }); }
return ( <> <input value={query} onChange={handleChange} /> {isPending && <span>updating…</span>} <List items={results} /> </> );}keystroke ไม่เคยถูก block เลย: ถ้าคุณพิมพ์อีกก่อน filter จะเสร็จ React จะทิ้ง render ของ filter ที่ค้างอยู่แล้วเริ่มอันใหม่ isPending เป็น true ระหว่างที่ transition กำลัง render
flowchart LR key["keystroke"] --> urgent["setQuery (urgent) input อัปเดตทันที"] key --> trans["startTransition(setResults) (non-urgent)"] trans --> bg["render เบื้องหลัง interrupt ได้โดย keystroke ถัดไป"]
useDeferredValue: defer ที่ค่า แทนที่จะ defer ที่ update
หัวข้อที่มีชื่อว่า “useDeferredValue: defer ที่ค่า แทนที่จะ defer ที่ update”useDeferredValue คือแนวคิดเดียวกันจากอีกด้าน แทนที่จะครอบ update คุณครอบ ค่า ที่คุณอ่าน: hook คืนค่าเวอร์ชันที่ “ตามหลัง” ระหว่างงาน urgent ดังนั้นส่วนที่แพงจะ re-render จากค่า deferred (ที่เก่าไปนิดชั่วขณะ)
function SearchResults({ allItems, query }) { const deferredQuery = useDeferredValue(query); // list render จาก deferredQuery ระหว่างที่ user พิมพ์ deferredQuery จะตามหลังหนึ่งก้าว // ทำให้การ render list ที่แพงเกิดเบื้องหลัง const results = filterExpensive(allItems, deferredQuery);
const isStale = query !== deferredQuery; return <List items={results} style={{ opacity: isStale ? 0.6 : 1 }} />;}อันไหนดี?
หัวข้อที่มีชื่อว่า “อันไหนดี?”- ใช้
useTransitionเมื่อ คุณเป็นเจ้าของ state update และครอบ update นั้นในstartTransitionได้ — คุณมี setter อยู่ตรงจุดที่เรียก - ใช้
useDeferredValueเมื่อค่ามาจาก นอกการควบคุมของคุณ (เป็น prop หรือ state ที่ครอบไม่ได้) — คุณ defer การอ่านค่านั้นแทน
ทั้งคู่ทำให้ input urgent ยัง responsive โดยปล่อยให้การ re-render ที่แพงเกิดเป็นงานเบื้องหลังที่ interrupt ได้ ทั้งคู่ ไม่ได้ ทำให้งานที่แพงเร็วขึ้น — เปลี่ยนแค่ priority ไม่ใช่ cost (สำหรับ list ที่ใหญ่จริง ๆ คุณยังต้องใช้ virtualization)