何をする Hook か
Server Action の戻り値をフォームの状態として管理します。バリデーションエラーや処理結果をクライアントで受け取り、UI に反映できます。
const [state, action, isPending] = useActionState(serverAction, initialState);
| 戻り値 | 内容 |
|---|---|
state | Server Action の最新の戻り値(初回は initialState) |
action | フォームの action に渡す関数 |
isPending | Server Action 実行中は true |
React バージョンとインポート元
| バージョン | Hook 名 | インポート元 |
|---|---|---|
| React 18 | useFormState | react-dom |
| React 19 以降 | useActionState | react |
Next.js 15 は React 19 を使うため useActionState を react からインポートします。
基本的な使い方
// app/actions.ts
"use server";
type State = { error?: string } | null;
export async function createPostAction(
_prevState: State,
formData: FormData
): Promise<State> {
const title = formData.get("title") as string;
if (!title) {
return { error: "タイトルを入力してください" }; // state に入る
}
await db.post.create({ data: { title } });
redirect("/posts"); // 成功時はリダイレクト
}
// components/CreatePostForm.tsx
"use client";
import { useActionState } from "react";
import { createPostAction } from "./actions";
export function CreatePostForm() {
const [state, action, isPending] = useActionState(createPostAction, null);
return (
<form action={action}>
{state?.error && (
<p className="text-sm text-red-500">{state.error}</p>
)}
<input name="title" required className="border px-3 py-2" />
<button type="submit" disabled={isPending}>
{isPending ? "作成中…" : "作成"}
</button>
</form>
);
}
Server Action の型シグネチャ
useActionState に渡す Server Action は第 1 引数に prevState を受け取る形にします。
// ✅ prevState を第 1 引数に追加する
export async function myAction(
prevState: State, // useActionState が前の state を渡す
formData: FormData // フォームデータ
): Promise<State> {
// ...
}
useFormStatus の pending との違い
useActionState の isPending | useFormStatus の pending | |
|---|---|---|
| 取得方法 | 戻り値の第 3 要素 | Hook の戻り値 |
| 使える場所 | useActionState を呼ぶコンポーネント | 親 <form> の子コンポーネント |
| 向いている場面 | フォーム全体が Client Component | ボタンだけを Client Component に切り出す場合 |
送信ボタンの状態を子コンポーネントで参照するには → useFormStatus
Server Actions の使い方 → Server Actions