✍️

🚀 React 19 | 新フック&フォーム管理の進化 💡 開発効率UPの最新機能まとめ

2025/02/17に公開

以下では、React19の背景や最新機能をざっと整理しながら、自分の備忘録としてもまとめてみました。

はじめに: 進化するウェブ開発のニーズ 👀

ここ数年、Webアプリ開発は以下のようなポイントを強く意識して作るケースが増えているように思います。

  • 初回ロードの速さ: 初回ロードから画面にコンテンツが表示されるまでの時間をいかに短くできるか。ユーザーが「待ち疲れ」しないようにする工夫が重要。
  • インタラクティブ性: アプリの規模が大きくなるほど、状態管理やリアルタイム更新が複雑になりがち。サクサク動くUIとストレスのない操作感を実現するのがポイント。
  • SEO対応: SPAでも検索エンジンにしっかりインデックスされるように、SSRやSSGの導入が一般的になりつつある。
  • ネットワークがよろしくない環境への対応: オフラインや低速回線でも、ユーザーを過度に待たせないためのキャッシュやロード分割などの工夫が重要に。

こういった要件をすべて満たすには、SSR/SSG/ISR といったレンダリング手法や、状態管理、パフォーマンス最適化など、幅広い知識が必要です。

その上で、Reactがこれまでどのように進化してきたのか 🏃

Reactは上記のニーズに応える形で、少しずつ進化してきました。

  • React16.8: これまでクラスコンポーネント中心だった状態管理やライフサイクルメソッドによる副作用処理に代わる手法として、Hooks(主に useState・useEffect など)が提案され、関数コンポーネントだけで状態や副作用をシンプルに扱えるようになり、ロジックの再利用性も飛躍的に向上しました。
  • React18: 複数のレンダリングタスクを効率的にスケジューリングできる同時レンダリング(Concurrent Rendering)や、複数のstate更新をまとめて処理する自動バッチ処理(Automatic Batching)が強化され、アプリケーション全体の描画がよりスムーズかつ効率的な仕組みに。
  • RSC(React Server Components)の登場とReact19: サーバー側でコンポーネントを処理してクライアントへは最小限のデータだけを送るRSC(React Server Components)という新たなアーキテクチャが登場。加えて、React19では、後述するフォーム管理機能のさらなる強化やサーバーサイド関連の拡張が進められています。

React19の注目機能 💡

今回のReact19では、これまでの進化に加え、多くのアップデートが実施されました。その中から、いくつか注目すべきポイントをピックアップします。

フォーム管理がシンプル化

React 19では、フォーム管理がさらに扱いやすくなりました。

  • <form action={非同期関数}>によるフォーム処理の簡素化
    actionにフォーム送信時の非同期関数を直接的に呼び出せるようになり、FormDataオブジェクトも自動で受け取れるようになったことで、フォーム送信の実装をよりシンプルに記述可能になりました。

  • useFormStatusフック
    フォームの送信中(pending)状態を子孫コンポーネントから簡単に取得できるため、ボタンのdisabled制御などを楽に行えます。UIの一貫した状態管理にも役立つ便利なフックです。

useTransitionのアクション対応

useTransitionのstartTransitionに渡すコールバックをasync関数として定義できるようになりました。これにより、以下のメリットがあります。
・非同期処理をスムーズにトランジション化して扱える
・処理開始時にisPending=true、完了後にfalseへ自動的に切り替わるため、ペンディング状態を手動で管理する手間が大幅に減少

function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

新フック: useActionState / useFormStatus / useOptimistic

  • useActionState
    ・非同期アクション(フォーム送信含む)の結果やエラー、そしてペンディング状態をまとめて管理。
    ・Canary版でuseFormStateと呼ばれていたものが最終的にuseActionStateに名称変更。
    ・<form action={submitAction}> に渡した際、送信完了やエラー、ロールバックなどを一括で管理できる。

  • useFormStatus
    ・<form> 要素のペンディング状態を子孫コンポーネントから参照可能。
    ・「送信中かどうか」だけを取得したい場合に特化。UI部品やデザインコンポーネントでのボタン制御などに便利。

  • useOptimistic
    ・楽観的UIを手軽に実装できるフック。ユーザー操作を即座にUI反映し、失敗時に元の状態へロールバックする処理をシンプルに書ける。

useフックで非同期データ取得がよりスッキリ

useを使うと Promiseを受け取って自動サスペンドしてくれるので、データが揃うまで <Suspense> を活用してローディング表示を出すことが簡単になります。

import { Suspense, use } from 'react';

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map(c => <p key={c.id}>{c.text}</p>);
}

function App({ commentsPromise }) {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  );
}

React19のコードサンプル集 📝

実際にReact19の新機能(主に新フック)を使うとどのような感じになるのか、いくつかコード例を記載します。

1. <form action={非同期関数}> でフォーム送信をシンプルに

async function handleSubmit(formData) {
  const newUser = {
    username: formData.get("username"),
    email: formData.get("email"),
  };

  try {
    const response = await fetch("https://api.example.com/users", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(newUser),
    });

    console.log("[Action] User successfully submitted:", newUser);
  } catch (error) {
    console.error("[Action] Failed to submit user:", error);
  }
}

function Action() {
  return (
    <>
      <h3>Action Example</h3>
      <form action={handleSubmit}>
        <div>
          <label>User Name</label>
          <input type="text" name="username" required />
        </div>
        <div>
          <label>Email</label>
          <input type="email" name="email" required />
        </div>
        <button type="submit">Submit</button>
      </form>
    </>
  );
}

export default Action;

ポイント:
・actionにフォーム送信時の非同期関数を直接呼び出せるようになり、FormDataオブジェクトも自動で受け取れるようになったことで、フォーム送信の実装がよりシンプルに記述可能に。
・useFormStatusと組み合わせることで送信中の状態を管理しやすい(ボタンの無効化や「送信中…」の表示が簡単に実装できる)。

2. useActionState を使ったフォーム送信の管理

import { useActionState } from "react";

async function handleProfileUpdate(prevState, formData) {
  const name = formData.get("username");
  if (name === "NOA") {
    return null;
  } else {
    return "Error: name must be 'NOA'.";
  }
}

function FormState() {
  const [message, submitAction, isPending] = useActionState(
    handleProfileUpdate,
    null
  );

  return (
    <>
      <h3>useActionState() example</h3>
      <form action={submitAction}>
        <label>Name</label>
        <input type="text" name="username" />
        <button disabled={isPending}>Submit</button>
        {message && <p>{message}</p>}
      </form>
    </>
  );
}

export default FormState;

ポイント:
・useActionStateは、アクション実行〜結果の受け取り(エラー含む)〜ペンディングを一括管理してくれる。
・メッセージやエラーをまとめて扱えるので、フォーム周りの実装がスッキリ。

3. useFormStatus でフォーム送信中の状態をかんたん検知

import { useFormStatus } from "react-dom";

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}

async function fakeAction() {
  // 2秒待機
  await new Promise((resolve) => setTimeout(resolve, 2000));
  console.log("[useFormStatus] Form submitted!");
}

function FormStatus() {
  return (
    <>
      <h3>useFormStatus() example</h3>
      <form action={fakeAction}>
        <SubmitButton />
      </form>
    </>
  );
}

export default FormStatus;

ポイント:
・<form> 送信が完了するまでpendingが自動でtrueになり、完了時にfalseに戻る。

4. useOptimisticで楽観的UIを簡単実装

import { useState, useOptimistic } from "react";

function Optimistic() {
  const [messages, setMessages] = useState([
    { text: "Initial message", sending: false },
  ]);

  // 楽観的UI反映のフック
  const [optimisticMsgs, addOptimisticMsg] = useOptimistic(
    messages,
    (currentMessages, newMessage) => [
      ...currentMessages,
      { text: newMessage, sending: true },
    ]
  );

  async function sendFormData(formData) {
    const msgText = formData.get("username");
    // 擬似的に1秒待ち(サーバー応答想定)
    await new Promise((res) => setTimeout(res, 1000));
    setMessages((prev) => [...prev, { text: msgText, sending: false }]);
  }

  async function handleSubmit(formData) {
    // 先にUIを更新しちゃう
    addOptimisticMsg(formData.get("username"));

    // 実際の送信
    await sendFormData(formData);
  }

  return (
    <>
      {optimisticMsgs.map((msg, i) => (
        <div key={i} style={{ fontWeight: "bold" }}>
          {msg.text}
          {msg.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={handleSubmit}>
        <h3>useOptimistic() example</h3>
        <div>
          <label>Username</label>
          <input type="text" name="username" />
        </div>
        <button type="submit">Send</button>
      </form>
    </>
  );
}

export default Optimistic;

ポイント:
・ユーザー操作→即座にUI更新→失敗時にロールバック、という「楽観的UI」をわずかなコードで実現。
・UX向上に繋がる機能を公式フックとしてサポートしているため、ロジックが散らばらず保守しやすい。

まとめ

React19では、フォーム管理を中心とした新機能(Actionsや新フック:useActionState,useFormStatus,useOptimistic など)が追加されました。これにより、ペンディング状態やエラー処理、楽観的UIの実装を公式機能として扱えるため、従来よりもシンプルかつ可読性の高いコードが書けます。さらにuseフックを用いた非同期データ取得や、React Server Components(RSC)の本格始動により、開発者体験とユーザー体験の両面で大きな進化を遂げています。すでにReact19の安定版もリリースされているため、対応可能な部分から積極的に取り入れていきたいですね!

Discussion