by MintJams

なぜ今すぐ使うべき?React 19のActionsとフォーム改善がもたらす開発革命

【React 19】Actionsとフォーム改善はもう使わなきゃ損。SSR/ストリーミングとの連携で、開発効率とUXを劇的に向上させる方法を実践コードで解説します。

React 19がもたらす革新的な変更の中でも、開発者が真っ先に恩恵を受けられるのがActionsと、それに伴うフォーム周りの改善です。これらは、単に新しいAPIが追加されたというだけでなく、サーバー関数と組み合わせることで、ユーザー体験を飛躍的に向上させ、開発プロセスをシンプルにします。特に、サーバーサイドでのデータ操作が絡むアプリケーションでは、これまでの実装とは一線を画すほど簡潔に記述できるようになります。この記事では、React 19のActionsとフォーム改善に焦点を絞り、明日からでも試せる実践的な使い方と、その真価を解説します。


本記事のコード例は、React Server Componentsをサポートするフレームワークとして、Next.jsのApp Router環境を前提としています。

React 19は、2024年12月5日に安定版として正式リリースされました。本記事の内容は安定版の仕様に基づいています。


この記事でわかること

  • React Actionsの基本的な使い方とサーバー関数との連携方法
  • useFormStatususeActionStateフックを使ったフォームの状態管理
  • フォームのネイティブ機能を活かしたデータ送信と状態更新のベストプラクティス
  • よくある問題と解決策、導入時のチェックポイント

React Actions:サーバーサイド連携の新しい形

React 19のActionsは、ユーザーの操作(フォーム送信など)に応じて関数を実行するための新しいパラダイムです。これは、従来のevent.preventDefault()を使ってfetch関数を呼び出す方法とは異なり、ネイティブなフォーム送信の挙動を活かしつつ、サーバー側のロジックを直接呼び出す仕組みです。

フォームが送信されると、React自体がサーバーとクライアント間の“橋渡し”役となり、サーバー関数を呼び出し、その結果に応じて自動的にUIを更新します。これにより、複雑な状態管理や副作用のハンドリングが不要になり、より宣言的なコード記述が可能になります。この機能は、特にストリーミングとの相性が抜群です。フォーム送信後に新しいデータをフェッチしてページ全体を再レンダリングするのではなく、必要な部分だけをサーバーからストリーミングで更新できるため、応答性が向上し、ユーザーは待ち時間をほとんど感じなくなります。例えば、To-Doリストに新しい項目を追加した直後に、リスト部分だけがスムーズに更新されるといった体験を実現できます。

サーバーアクションを定義してみよう

サーバーアクションは、'use server'ディレクティブをファイルの先頭に記述することで定義できます。

// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';

type TodoResult = {
  success: boolean;
  message?: string;
};

export async function createTodo(formData: FormData): Promise<TodoResult> {
  // フォームデータからタイトルを取得
  const title = formData.get('title');
  if (!title) {
    return { success: false, message: 'タイトルは必須です。' };
  }

  console.log('Todo:', title);

  // データベースへの書き込み処理(例)
  await new Promise(resolve => setTimeout(resolve, 1000));

  // データを再検証して、UIを自動的に更新する
  revalidatePath('/todos');

  return { success: true, message: 'Todoが追加されました!' };
}

このcreateTodo関数は、フォームが送信されると自動的に実行されます。formDataは、フォームのname属性とvalueをキーと値に持つFormDataオブジェクトです。

次に、このサーバーアクションをコンポーネントで利用します。

// app/page.tsx
import { createTodo } from './actions';

export default function Page() {
  return (
    <div>
      <h1>新しいTodoを作成</h1>
      <form action={createTodo}>
        <input type="text" name="title" required />
        <button type="submit">Todoを追加</button>
      </form>
    </div>
  );
}

<form>要素のactionプロパティにcreateTodo関数を渡すだけで、あとはReactが自動で処理してくれます。これまでのonSubmitハンドラでfetchを呼び出す実装と比べて、信じられないほどシンプルになったことがわかるでしょう。


フォーム状態管理の新フック

Actionsの導入に伴い、フォームの状態を扱うための新しいフックも追加されました。これらは、フォームの送信状態やサーバーアクションの結果を簡単に取得できます。

useFormStatus

useFormStatusフックは、親の<form>要素の送信状態を取得します。これにより、フォームが送信中かどうかを判断し、例えばボタンを無効化するなどのUIフィードバックを実装できます。pendingtrueの間に、送信ボタンを無効化したり、スケルトンUIを表示したりすることで、ユーザー体験がさらに向上します。

useFormStatusは、pending以外にも、フォームのactionmethod、送信データであるdataといったプロパティも提供します。

// SubmitButton.tsx
'use client';
import { useFormStatus } from 'react-dom';

export function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending} aria-busy={pending}>
      {pending ? '送信中...' : '投稿'}
    </button>
  );
}

useFormStatusは、クライアントコンポーネントであり、<form>の子孫でのみ有効である点に注意が必要です。

useActionState

useActionStateフックは、アクション関数の状態を追跡し、サーバーからのレスポンスを保持します。これは、React 19でuseFormStateから改名されたフックです。これにより、フォームのバリデーションエラーメッセージを表示するなど、アクションの結果に応じたUI更新が可能になります。

useActionStateは、[state, formAction, pending]という配列を返します。pendingは第三戻り値(第三要素)として含まれており、ローディング表示に利用できます。

'use client';
import { useActionState } from 'react';
import { createPost } from './actions';

const initialState = { message: null };

export default function PostForm() {
  const [state, formAction, pending] = useActionState(createPost, initialState);

  return (
    <form action={formAction}>
      <input name="title" required />
      <button type="submit" disabled={pending}>投稿</button>
      {state?.message && <p aria-live="polite">{state.message}</p>}
    </form>
  );
}

useActionStateは、アクション関数と初期状態を引数にとり、[state, formAction, pending]の配列を返します。アクション関数のシグネチャが (prevState, formData) となる点も特徴です。formAction<form>actionに渡すことで、アクション関数の結果がstateに自動的に反映されます。

どちらを使うべきか迷ったら?

送信中のUIフィードバックにはuseFormStatusを、送信結果やエラーメッセージの表示にはuseActionStateを使うのが基本です。


トラブル時のチェックポイント

もしActionsがうまく動作しない場合は、以下の点を確認してみてください。

  • 'use server'ディレクティブの記述: サーバーアクションを定義したファイルの先頭に'use server'が正しく記述されているか?
  • FormDataオブジェクトの確認: サーバーアクションの引数としてformDataを受け取っているか?formData.get('name')で正しい値が取得できるか?
  • サーバー関数を利用しているか: <form action={someClientFunction}>のように、クライアント関数をactionに渡しても、クライアント側で動作はしますが、サーバー実行の恩恵は得られません。'use server'を付けたサーバー関数を渡すようにしてください。
  • SSR環境での動作: <form action={fn}>サーバー関数を渡すことで、サーバー実行やストリーミングのメリットが生まれます。これらの恩恵を享受するには、Next.jsのようなSSRフレームワークとの連携が不可欠です。

チェックリスト:React 19 Actions導入の第一歩

  1. 環境の準備: Next.js App Routerのような、React Server Componentsをサポートする環境をセットアップする。
  2. サーバーアクションの定義: 新しい'use server'ディレクティブを使って、サーバーサイドで実行したい関数を作成する。
  3. フォームへの紐付け: <form action={yourServerAction}>のように、フォームのactionプロパティにサーバーアクションを渡す。
  4. UIフィードバックの追加: useFormStatusを使って送信中のUIを実装する。
  5. 状態管理の簡略化: useActionStateを使って、アクションの結果に応じたUIを更新する。

まとめ

React 19のActionsとフォーム改善は、単なる機能追加ではなく、サーバーサイド連携のあり方そのものを変える大きな一歩です。これにより、開発者は煩雑なAPI呼び出しや状態管理から解放され、より本質的なビジネスロジックに集中できます。特にサーバー関数とストリーミングを組み合わせることで、ユーザー体験の向上と開発効率の両方を高めることができるでしょう。これらの新機能を使いこなすことで、あなたの開発スキルはさらに一段上のレベルへと引き上げられます。

この記事がReact 19の理解を深める一助となれば幸いです。ご覧いただきありがとうございました。


参考資料