12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React Router V7に入門してみた ~Actionでフォームのイベントを管理する~

12
Last updated at Posted at 2026-04-03

はじめに

前回の記事では、データ取得を loader に任せる方法を解説しました。
今回はその対になる機能、「データの更新(作成・編集・削除)」 を担う action について解説します。

この記事で分かること

  • React Router v7 における action の基礎概念
  • <Form> コンポーネントと action を組み合わせたデータ送信方法
  • 送信後の自動Re-Validation(データの再取得)の仕組み
    • 言葉の意味としてzodのvalidation(検証)と被りがちなのでややこしい…

action による宣言的なデータ更新

従来のReactでは、フォーム送信時にonSubmitでデータ更新処理を呼び出しuseStateで管理している値をfetchaxiosで送信するのが一般的でした。

React Router v7 の Data モードでは、action という関数を定義することで、ブラウザの標準的なフォーム送信に近い挙動を、JSの力で最適化して実行できます。

action の大きなメリット:自動リバリデーション

action が完了すると、React Router は現在画面に表示されている すべての loader を自動的に再実行します。

これにより、データを更新した後に

  • 手動でステートを更新する
  • 再度 API を叩きに行く
  • 画面をリロードする

といった処理を書く必要がなくなり、 画面上のデータが常に最新の状態に同期されます。

実装例:ユーザー情報の更新

特定のユーザーの名前を更新する簡単なフォームを例に解説します。

1. action の作成

送信されたフォームデータを受け取り、API に投げる action を定義します。

src/actions/userUpdateAction.ts
import { ActionFunctionArgs, redirect } from "react-router";

export async function userUpdateAction({ request, params }: ActionFunctionArgs) {
  // 1. フォームデータの取得
  const formData = await request.formData();
  const updates = Object.fromEntries(formData);

  // 2. APIへのリクエスト(例)
  await fetch(`https://jsonplaceholder.typicode.com/users/${params.userId}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(updates),
  });

  // 3. 更新後は詳細画面などにリダイレクト
  return redirect(`/users/${params.userId}`);
}

2. Component の作成(Form コンポーネント)

React Router が提供する <Form> コンポーネントを使用します(ある程度複雑なアプリではReact-Hook-Formを使うことになると思います)。これを使うことで、複雑な onSubmit ハンドラは不要になります。

src/pages/UserEdit.tsx
import { Form, useLoaderData, useNavigation } from "react-router";

export default function UserEdit() {
  const user = useLoaderData() as any;
  const navigation = useNavigation();

  // 送信中の状態を判定
  const isSubmitting = navigation.state === "submitting";

  return (
    <div>
      <h2>ユーザー編集</h2>
      <Form method="post">
        <label>
          名前:
          <input type="text" name="name" defaultValue={user.name} />
        </label>
        <button type="submit" disabled={isSubmitting}>
          {isSubmitting ? "保存中..." : "保存"}
        </button>
      </Form>
    </div>
  );
}

3. Route Object の定義(main.tsx)

loader と同様に、ルート定義に action を紐付けます。

src/main.tsx
import { createBrowserRouter, RouterProvider } from "react-router";
import RootLayout from "./layouts/RootLayout";
import App from "./App";
import UserDetails from "./pages/UserDetails";
import { userDetailsLoader } from "./loaders/userDetailsLoader";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import UserEdit from "./pages/UserEdit";
import { userUpdateAction } from "./actions/userUpdateAction";

const router = createBrowserRouter([
  {
    path: "/",
    Component: RootLayout,
    children: [
      {
        index: true, 
        Component: App,
      },
      {
        path: "users/:userId",
        Component: UserEdit,
        loader: userDetailsLoader,
        action: userUpdateAction,
      },
      {
        path: "users",
        Component: UserDetails,
        loader: userDetailsLoader,
      },
    ],
  },
]);

const root = document.getElementById("root");

createRoot(root!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>
)

まとめ

  • actionデータ更新の責任をコンポーネントから分離 できる(すごい :tada:
  • <Form> を使うことで、preventDefaultuseState 管理から解放される
  • Re-Validation のおかげで、更新後のデータ整合性を気にする必要がなくなる

loader で取得し、action で更新し、再度loaderを呼び出す。このサイクルに乗ることで、コンポーネントのコードがシンプルになります。

蛇足

公式ドキュメントにある通り、Loaderを呼び出すかどうかの調整も可能です。また、Loaderが再度読み込まれるのは次のような場合です。

A route loader is revalidated when:

  • its own route params change
  • any change to URL search params
  • after an action is called and returns a non-error status code

参考文献

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?