use() & Data Fetching
use() API ของ React 19
หัวข้อที่มีชื่อว่า “use() API ของ React 19”use() เป็น API ของ React 19 ที่ อ่านค่าของ resource ระหว่าง render — ที่สำคัญที่สุดคือ promise หรือ context เมื่อคุณส่ง promise เข้าไป component จะ suspend ระหว่างที่ promise ยัง pending (แสดง Suspense fallback ที่ใกล้ที่สุด) และคืนค่าที่ resolve แล้วเมื่อ promise settle
import { use, Suspense } from "react";
function Message({ messagePromise }) { const text = use(messagePromise); // suspend จนกว่า promise จะ resolve return <p>{text}</p>;}
function Container({ messagePromise }) { return ( <Suspense fallback={<p>Loading…</p>}> <Message messagePromise={messagePromise} /> </Suspense> );}use() แหกกฎข้อหนึ่งที่คุณเรียนกับ hook อื่น: คุณ เรียกแบบมีเงื่อนไขได้ — ใน if หรือใน loop ที่เป็นแบบนั้นได้เพราะไม่ได้ผูกกับ hook slot ตายตัวแบบ useState/useEffect
กฎสำคัญ: cache promise ไว้
หัวข้อที่มีชื่อว่า “กฎสำคัญ: cache promise ไว้”use() ไม่ เริ่ม fetch และไม่ cache อะไรเลย แค่อ่าน promise ที่คุณให้ — ดังนั้น promise นั้น ต้องเสถียรข้าม render การสร้าง promise ใน body ของ component หมายถึง promise ใหม่ทุก render ซึ่งจะ suspend ตลอดกาล (หรือ refetch ไม่หยุด)
// ❌ promise ใหม่ทุก render — refetch / ไม่ settlefunction Bad({ id }) { const data = use(fetch(`/api/${id}`).then(r => r.json())); // ...}
// ✅ promise ถูกสร้างข้างนอกและส่งเข้ามา (cache โดย caller/framework)function Good({ dataPromise }) { const data = use(dataPromise); // ...}นี่คือเหตุผลที่ use() ดิบ ๆ มักจับคู่กับ cache เสมอ — ไม่ว่าจะเป็น data layer ของ framework หรือ Map cache ที่ทำเอง — ที่คืน promise instance เดิม สำหรับ request เดิม
Render-as-you-fetch
หัวข้อที่มีชื่อว่า “Render-as-you-fetch”pattern ที่ use() เปิดทางให้คือ render-as-you-fetch: เริ่ม request ก่อนหรือขณะ ที่คุณ render (เช่นใน route loader หรือตอน hover) ส่ง promise ลงไป แล้วปล่อยให้ component อ่าน promise นั้นผ่าน use() ใน Suspense boundary เทียบสามแนวทาง:
flowchart TB
subgraph old["fetch-on-render (useEffect)"]
r1["render"] --> e1["effect รัน"] --> f1["แล้ว fetch ค่อยเริ่ม"] --> r2["re-render พร้อม data"]
end
subgraph new["render-as-you-fetch (use + Suspense)"]
start["เริ่ม fetch ก่อน
(loader / event)"] --> pass["ส่ง promise ลงไป"]
pass --> read["use(promise) suspend
→ Suspense fallback → data"]
end use() ยังอ่าน context ได้ด้วย และต่างจาก useContext ตรงที่เรียกแบบมีเงื่อนไขได้ — สะดวกสำหรับอ่าน context ใน branch
ปกติคุณจะไม่เขียนเองด้วยมือ
หัวข้อที่มีชื่อว่า “ปกติคุณจะไม่เขียนเองด้วยมือ”นี่คือส่วนที่ต้องซื่อสัตย์ use() ดิบ ๆ + Suspense + promise cache ที่เขียนเองนั้นทรงพลังแต่ระดับต่ำ ในแอปจริงคุณเกือบทุกครั้งจะได้ render-as-you-fetch ผ่าน framework (router/loader ที่ fetch ต่อ route และให้ cache) หรือ data library อย่าง TanStack Query (ที่จัดการ caching, revalidation และ Suspense integration ให้) เรียน use() เพื่อเข้าใจกลไก; ใช้ library เพื่อ ship จริง