ข้ามไปยังเนื้อหา

Transitions

ลองนึกถึง search box ที่ filter list ยักษ์ ทุก keystroke อัปเดตสองอย่าง: ค่าใน input (ต้องรู้สึกทันที) และผลลัพธ์ที่ filter แล้ว (คำนวณแพง) ถ้าทั้งคู่ urgent การ filter ที่แพงจะ block keystroke และพิมพ์จะสะดุด

Transitions ให้คุณแยกสองอย่างนี้ออก: keystroke ยัง urgent ส่วนการ filter กลายเป็น transition priority ต่ำที่ React interrupt ได้เมื่อ keystroke ถัดไปมา

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 ถัดไป"]
urgent update ลงก่อน แล้ว transition ค่อย render ทีหลัง

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)

การครอบ state update ไว้ใน `startTransition` ทำอะไร?
`useTransition` คืนอะไร?
เมื่อไร `useDeferredValue` เหมาะกว่า `useTransition`?
transition ทำให้งานที่แพงเร็วขึ้นไหม?