【React19】フォーム今昔比較【useActionState】

2026年現在、フロントエンドフレームワークの進化はすさまじく、「昔はこう書いてたのになー」と思うこともしばしば出てくるようになりました。
Reactは2025年12月にバージョン19がリリースされ、フォームや非同期UIに関わる大きな新機能として、主に次の機能が追加されました。
  • Actions の正式サポート
    • \<form action={action}>
    • useActionState
    • useOptimistic
    • useFormStatus
  • Suspenseを用いた非同期UIの簡略化
    • use
    • Suspense
今回はIT研修講師として登壇する中で、「今はこんなにすっきりしたんですね!!」とよく言われる、React19で追加された新規機能のuseActionStateを紹介したいと思います。

 

本記事の対象となる方

  • バージョン18以前のReactでフォームを実装したことがある方
  • Reactの最新機能の概要を知りたい方
  • Reactの「昔の書き方」と「今の書き方」の違いを理解したい方

 

Actionって何?

useActionStateを紹介する前に、Actionについて説明します。
Actionとは、Reactによって実行状態を管理しながら実行される関数のことです。
……いまいちイメージしづらいですよね。
もう少し嚙み砕くと、Reactが 「今この処理を実行中かどうか」を把握しながら実行できる関数のことです。
 
例えば、フォーム送信を考えてみましょう。
ユーザーが送信ボタンを押したら「送信中」と表示し、フォームの処理が終了すれば送信中の文字を消すような仕組みがあると、ユーザーはきちんとフォームが送信されているのか、フォームの処理が完了したか確認できて便利ですよね。
Actionを利用してHookを実装すると、関数の実行状態を参照した処理を実装できます。

 

useActionState:フォームでの活用

いよいよ、useActionStateに話を移しましょう。
useActionStateとは、実行状態だけでなくエラー処理や入力処理等、Actionに関連する状態をまとめて定義するHookのことです。
フォームで最も利用され、フォーム送信処理と、その結果として更新される状態を一つの流れとして記述できます。
これまでは、Reactでフォームを実装する際、下記の様な煩雑さがありました。
  • 多くの状態管理をしなければならない
    • 入力欄
    • ローディング
    • エラーメッセージ
    • 成功メッセージ
  • フォーム送信処理と、状態更新の記述が分散しやすい

useActionStateを使うことで、フォーム送信処理やその実行結果の状態をまとめて管理し、コードを簡潔に書けるようになりました。

また、React19では、
<form action={action}>
という書き方が可能になりました。
\<form> 要素のaction属性にActionを渡すことで、フォーム送信時にActionが呼ばれます。
これにより、フォームの実行状態を管理できるようになるだけでなく、FormDataをイベントオブジェクトから手動で取り出す必要がなくなり、Actionには入力内容が引数としてそのまま渡されます。

 

useActionStateがないフォーム

ユーザー登録画面を想定します。
  • 1つの入力欄のために4つの状態を宣言する
  • 全部で5つの状態更新ポイントを手動で設定⇒設定漏れが意図しない挙動につながる

import { useState } from "react";

export default function RegisterFormBefore() {
  // フォームに必要な状態を定義
  const [name, setName] = useState(""); // 入力欄
  const [loading, setLoading] = useState(false); // ローディング
  const [error, setError] = useState(""); // エラーメッセージ
  const [message, setMessage] = useState(""); // 成功メッセージ

  // フォーム送信処理を行うメソッド。処理の中で、状態を手動で更新する。
  const handleSubmit = async (e) => {
    e.preventDefault();

    // 状態更新①
    setLoading(true);
    setError("");
    setMessage("");

    try {
      const res = await fetch("http://example.com/user", {
        method: "POST",
        body: JSON.stringify({ name }),
      });

      if (!res.ok) {
        throw new Error("登録に失敗しました");
      }

      // 状態更新②
      setMessage("登録しました"); // 正常処理レスポンスをセット
      setName(""); // フォームの入力内容をリセット
    } catch {
      // 状態更新③
      setError("登録できませんでした"); // エラーレスポンスをセット
    } finally {
      // 状態更新④
      setLoading(false); // 通信終了状態にする
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={e => setName(e.target.value)} // 状態更新⑤
      />

      <button disabled={loading}>
        {loading ? "送信中..." : "登録"}
      </button>

      {error && <p style={{ color: "red"}}>{error}</p>}
      {message && <p style={{ color: "green"}}>{message}</p>}
    </form>
  );
}

useActionStateを使った場合

フォームに関する状態をまとめて定義し、Actionの戻り値や実行状態が自動的に画面に反映されることで、状態の定義漏れや変更漏れを防げます。

import { useActionState } from "react";

export default function RegisterFormAfter() {
  // Actionの定義
  const register = async (previousState, formData) => {
    const name = formData.get("name"); // formの入力値をそのまま使用
    const res = await fetch("http://example.com/user", {
      method: "POST",
      body: JSON.stringify({ name }),
    });
    
    if (!res.ok) {
      return {
        success: false,
        message: "登録に失敗しました",
      };
    }
    return {
      success: true,
      message: "登録しました",
    };
  }

  // 使用するActionと、Actionの戻り値となる状態の初期値をuseActionStateに渡す
  // stateにはActionの戻り値が、formActionにはstateとloadingが紐づいた関数が、
  // loadingにはAcionの実行状態が定義される
  // Actionの戻り値や実行状態は、Reactによって自動的に画面に反映される
  const [state, formAction, loading] = useActionState(register, {
    success: false, // 通信の成否
    message: "", // レスポンスメッセージ
  });

  // useActionStateではFormDataから値を取得するため、
  // ブラウザのフォーム機能を利用したシンプルな実装する
  return (
    <form action={formAction}>
      <input name="name" />

      <button disabled={loading}>
        {loading ? "送信中..." : "登録"}
      </button>

      {state.message && (
        <p style={{ color: state.success ? "green" : "red" }}>
          {state.message}
        </p>
      )}
    </form>
  );
}

 

大量のuseStateからの卒業

useActionStateは単にコード量を減らすためのHookではなく、 フォーム送信に関する状態管理を一か所にまとめるためのHookです。状態管理を一か所にまとめ、画面への自動反映もReactに任せられて便利なため、フォーム実装の際にはぜひ積極的に使っていきましょう。

 

人間も学ばなければいけない時代

昨今AIがコードを書く機会が増え、人間はそのコードを設計・レビュー・改善する役割がより重要になってきています。
しかし、それは人が学ばなくてもいい時代になったのではなく、適切なコードを判断できる技術を持ったエンジニアが必要になったというだけです。

 

株式会社カサレアルでは、最先端のバージョンを取り入れた、React入門講座を開講しています。
簡単なアプリケーションを作成する過程を追いながら、Reactの使い方をマスターしていく講座で、JavaScriptの基本的な文法の確認に加え、Reactの文法の基礎から状態管理、そして今回取り扱ったuseActionStateのほかに、React19で実装されたuseやSuspenseを用いた非同期UIの実装など、最新バージョンに対応した最先端の内容まで盛りだくさんの内容になっております。
初めての方からから学び直しの方まで、JavaScriptフレームワーク(ライブラリ)のデファクトスタンダードになりつつあるReactを、今こそ学んでみませんか。講師として皆さんとお会いできることを、心よりお待ちしております。

 

講座詳細、開催日程は下記リンクよりお確かめください↓
https://learning.casareal.co.jp/2011a-online
--------------------------
開発支援・技術研修のご要望・ご相談はこちらから
--------------------------
【この技術ブログを読んだエンジニアの皆様へ】
カサレアルブログをお読みいただき、ありがとうございます!

私たちは、常に新しい技術に挑戦し、ユーザーのニーズに応えるサービスを提供しています。
もし、当社の技術への情熱や、会社・チーム・社員の雰囲気に共感いただけたなら、
ぜひ私たちと一緒に働きませんか?
現在、株式会社カサレアルでは事業拡大に伴い、新たな仲間となるエンジニアを積極的に募集しています。

少しでも興味をお持ちいただけましたら、まずは弊社のことを知っていただけると嬉しいです。
▼採用サイト
https://www.casareal.co.jp/recruit/career
▼社員インタビュー
https://hrmos.co/pages/casareal/jobs/0000016
▼エンジニアの仲間になる! エントリーはこちらから
https://hrmos.co/pages/casareal/jobs

皆様のエントリーを心よりお待ちしています!

「Excel VBA (マクロ)」から「Power Automate (パワーオートメイト)」への移行メリット
Claudeのモデルを整理してみた|モデルの違いとプラン別の使い分け

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

コメント ※

名前 ※

メール ※

サイト