React 19の「useActionState」で、副作用の地獄から脱出する
Reactの現場で「副作用」という言葉を聞いて、胸が苦しくなったことはないだろうか?
これまで、フォーム送信後の状態管理といえば、`useState`でローカル状態を作り、`useEffect`で送信の成功・失敗を監視し、その中でトーストを表示したりリダイレクトしたり……という「副作用のパッチワーク」が定番だった。しかし、この手法は依存配列の管理ミスによる無限ループや、クリーンアップ関数の書き忘れによるメモリリークの温床になりがちだ。
React 19で導入された `useActionState` は、まさにこの「フォームにおける副作用管理」の苦しみに対する、Reactチームからの決定的な回答だ。今日は、このフックをどう実務で使いこなし、コードをクリーンに保つか、その核心を解説しよう。
—
なぜ今、useActionStateなのか?
これまで我々が書いていた「非同期処理の結果を待ってから何かをする」というロジックは、本質的に「宣言的」ではなかった。`useEffect` の中でアクションを叩き、その結果を `useState` に反映させる……これはReactのレンダリングサイクルを裏口から操作するようなもので、タイミングの制御に常に気を使わなければならない。
`useActionState` は、「アクション(関数)の実行結果」と「現在の状態」を同期させるという、極めて純粋な宣言的モデルを採用している。
ブラウザの裏側では、React 19の内部でトランジション(`useTransition`)が統合されており、送信ボタンを押した瞬間に「ペンディング状態(`isPending`)」が自動的に管理される。これによって、ローディング中の二重送信防止や、UIのレスポンシブな更新が、泥臭いフラグ管理なしに実現できるようになったんだ。
—
実践:堅牢なフォーム管理のサンプル
まずは、現場ですぐに使えるモダンなフォーム実装を見てほしい。
import { useActionState } from “react”;
// サーバーアクションを想定した非同期関数
// 実際にはAPIリクエストなどをここで行う
async function submitForm(prevState: any, formData: FormData) {
const email = formData.get(“email”);
// バリデーションのシミュレーション
if (!email || !email.toString().includes(“@”)) {
return { status: “error”, message: “正しいメールアドレスを入力してください。” };
}
// 非同期処理を模倣
await new Promise((resolve) => setTimeout(resolve, 1000));
return { status: “success”, message: “登録が完了しました!” };
}
export function SubscriptionForm() {
// useActionStateの引数は (アクション関数, 初期状態)
// 戻り値は [現在の状態, アクション実行関数, 保留中フラグ]
const [state, formAction, isPending] = useActionState(submitForm, {
status: “idle”,
message: “”,
});
return (
);
}
このコードが「現場の最適解」である理由
1. 副作用の局所化: `useEffect` を使わずに、フォームの送信結果を直接UIにマッピングしている。これにより、「あのエフェクト、どのタイミングで走るんだっけ?」と頭を抱える必要がなくなる。
2. Pending管理の標準化: `isPending` が標準で提供されるため、ボタンの無効化やローディングUIの切り替えが標準化される。チーム開発において、各々が独自の `isLoading` フラグを作ってしまうという非効率を排除できる。
3. 予測可能な状態遷移: `prevState` が常に引数として渡ってくるため、エラーからの復帰や状態の積み上げが非常に自然に行える。
—
チーフアーキテクトからのアドバイス
実務でこのフックを使う際、一点だけ注意してほしい。「何でもかんでも `useActionState` に詰め込まないこと」だ。
もし、送信後に「外部ライブラリのモーダルを開く」「分析ツール(GAなど)へイベントを送る」といった、フォームの「状態」とは別の「副作用」が必要な場合は、`useEffect` と `useActionState` を適材適所で使い分ける必要がある。
`useActionState` はあくまで「フォームの状態管理」を宣言的にするための道具だ。それを過信して、UIの副作用まで全てを強引にこの枠組みに押し込もうとすると、逆にコードが歪むことがある。
「状態は `useActionState` で持ち、UIのトリガーとしての副作用は `useEffect` で受ける」。この役割分担を意識するだけで、君の書くコードの品質は一段上のレベルに達するはずだ。
React 19は、我々から「副作用の苦悩」を少しずつ取り除いてくれている。この新しい道具を使いこなし、ぜひ「保守しやすく、かつ直感的なUI」を追求してほしい。健闘を祈る。

コメント