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

Forms & useOptimistic

บ่อยครั้งคุณอยากให้ submit button รู้ว่า form ของตัวเอง pending อยู่ไหม — เพื่อ disable ตัวเองและแสดง spinner การส่ง isPending ลงไปทุก component ระหว่างทางน่าเบื่อ useFormStatus (จาก react-dom) ให้ component อ่าน status ของ <form> ที่เป็น parent ที่ใกล้ที่สุดได้ตรง ๆ:

import { useFormStatus } from 'react-dom';
// This button reads the enclosing form's status — no props needed.
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving…' : 'Save'}
</button>
);
}
function ProfileForm({ action }) {
return (
<form action={action}>
<input name="name" />
<SubmitButton /> {/* knows the form is pending, with zero prop drilling */}
</form>
);
}

กฎเดียว: useFormStatus อ่าน form ที่เป็น parent ดังนั้น SubmitButton ต้องถูก render อยู่ข้างใน <form> ไม่ใช่เป็น component ที่ render form เอง

เพื่อให้ UX กระฉับกระเฉง คุณมักอยากแสดงผลลัพธ์ที่ คาดว่าจะเกิด ทันทีและแก้ก็ต่อเมื่อ request ล้มเหลว useOptimistic ให้ optimistic value ที่คุณ set ทันทีและ React จะ revert กลับไปเป็นค่าจริงอัตโนมัติเมื่อ action จบ:

import { useOptimistic } from 'react';
function Thread({ messages, sendMessage }) {
const [optimisticMessages, addOptimistic] = useOptimistic(
messages,
(current, newText) => [...current, { text: newText, sending: true }],
);
async function formAction(formData) {
const text = formData.get('message');
addOptimistic(text); // show it immediately, marked "sending"
await sendMessage(text); // real request; React reconciles when it resolves
}
return (
<>
{optimisticMessages.map((m, i) => (
<p key={i}>{m.text} {m.sending && <small>(sending…)</small>}</p>
))}
<form action={formAction}>
<input name="message" />
<button type="submit">Send</button>
</form>
</>
);
}

message ปรากฏทันทีที่คุณ submit เมื่อ sendMessage resolve, React จะ drop entry ที่เป็น optimistic แล้วใช้ state จริงแทน; ถ้า sendMessage ล้มเหลว React จะ revert อัตโนมัติ คุณจึงไม่ต้อง roll back เอง

sequenceDiagram
  participant U as User
  participant O as useOptimistic
  participant S as server action
  U->>O: submit (addOptimistic)
  O->>U: show optimistic value instantly
  O->>S: await real request
  S-->>O: success then keep real state
  Note over O,U: on failure React reverts automatically
optimistic update แล้วค่อย settle

สอง hook นี้รวมกันทำให้ form รู้สึกทันใจ: useOptimistic สำหรับ feedback ทันที, useFormStatus สำหรับ affordance เรื่อง pending บนปุ่ม — ทั้งคู่โดยไม่ต้องร้อย state ผ่าน props

`useFormStatus` ให้อะไรกับ component?
component ที่เรียก useFormStatus ต้องถูก render ที่ไหน?
`useOptimistic` ทำอะไรเมื่อ async action ล้มเหลว?