何をする Hook か
親フォームの送信状態を子コンポーネントから参照します。送信中にボタンを無効化したりローディング表示を出したりする場面で使います。
import { useFormStatus } from "react-dom";
const { pending, data, method, action } = useFormStatus();
| フィールド | 内容 |
|---|---|
pending | フォーム送信中は true |
data | 送信中の FormData(送信完了後は null) |
method | フォームの HTTP メソッド |
action | フォームの action 属性の値 |
典型パターン:ボタンを Client Component に切り出す
フォーム全体を "use client" にせず、送信ボタンだけを Client Component に切り出すパターンが最もよく使われます。
// components/SubmitButton.tsx
"use client";
import { useFormStatus } from "react-dom";
export function SubmitButton({ label }: { label: string }) {
const { pending } = useFormStatus();
return (
<button
type="submit"
disabled={pending}
className="rounded bg-blue-600 px-4 py-2 text-white disabled:opacity-50"
>
{pending ? "送信中…" : label}
</button>
);
}
// app/posts/new/page.tsx(Server Component のままでよい)
import { createPostAction } from "./actions";
import { SubmitButton } from "@/components/SubmitButton";
export default function NewPostPage() {
return (
<form action={createPostAction}>
<input name="title" required />
<SubmitButton label="作成" />
</form>
);
}
注意:親フォームの直接の子でないと機能しない
useFormStatus は 呼ばれているコンポーネントの外側にある <form> の状態を参照します。フォームと同じコンポーネント内に書いても機能しません。
// ❌ フォームと同じコンポーネントで useFormStatus を使っても pending にならない
"use client";
export function MyForm() {
const { pending } = useFormStatus(); // ← この form の pending は取れない
return (
<form action={myAction}>
<button disabled={pending}>送信</button>
</form>
);
}
// ✅ 子コンポーネントに切り出す
"use client";
function SubmitButton() {
const { pending } = useFormStatus(); // ← 親の form の pending が取れる
return <button disabled={pending}>送信</button>;
}
useActionState の isPending との使い分け
フォーム全体がすでに "use client" になっている場合は、useActionState の isPending を使う方がシンプルです。
// フォーム全体が Client Component なら useActionState の isPending で十分
const [state, action, isPending] = useActionState(myAction, null);
return (
<form action={action}>
<button disabled={isPending}>送信</button>
</form>
);
フォームの状態・エラーを受け取るには → useActionState
Server Actions の使い方 → Server Actions