React Routerのaction完全攻略!実践で役立つデータ処理術
By Sora更新: 10/17(金) 20:15

React Routerのactionとは?データ処理の新しい常識
React Router v6.4以降で導入されたaction
は、Webアプリケーションにおけるフォーム送信やデータ変更処理(POST、PUT、PATCH、DELETE)を効率的に扱うための強力な機能です。これまでのReact開発では、フォームのデータ処理はコンポーネント内でState管理やイベントハンドラを使って実装されることが一般的でした。しかし、action
を利用することで、データ変更ロジックをルーティング層に集約し、より宣言的かつ整理されたコードを書くことが可能になります。
action
の主なメリットは以下の通りです。
- コードの可読性と保守性の向上: データ変更ロジックがルーティング定義に紐づくため、コンポーネントがデータの表示に専念でき、責務が明確になります。
- 自動的なデータ再検証 (Revalidation):
action
が完了すると、関連するloader
が自動的に再実行され、UIに表示されるデータが常に最新の状態に保たれます。これにより、手動でのデータ更新処理が不要になります。 - 保留中のUI (Pending UI) の管理: フォーム送信中の状態を簡単に検知し、ローディングインジケーターの表示やボタンの無効化といったユーザー体験向上のためのUIを実装できます。
この機能は、Remixフレームワークの思想をReact Routerに取り入れたもので、より強力なデータ管理能力をReactアプリケーションにもたらします。
actionの基本的な使い方:フォーム送信とデータ処理
action
は主に、<Form>
コンポーネントからのフォーム送信を処理するために使用されます。基本的な流れは、ルート定義でaction
関数をエクスポートし、コンポーネント内で<Form>
を使ってデータを送信するというものです。action
関数は、フォームから送信されたデータをrequest
オブジェクトとして受け取ります。
ルート定義の例
import { createBrowserRouter, RouterProvider, redirect } from 'react-router-dom';
// ユーザー作成ページのアクション
async function createUserAction({ request }) {
const formData = await request.formData();
const name = formData.get('name');
const email = formData.get('email');
// ここでAPIにデータを送信するなどの処理を行います。
console.log('ユーザーデータ:', { name, email });
// 処理が成功したら、別のページにリダイレクトします。
return redirect('/users');
}
const router = createBrowserRouter([
{
path: '/',
element: <div>Home</div>,
},
{
path: '/users/new',
element: <NewUserPage />,
action: createUserAction, // ルートにactionを紐付けます
},
{
path: '/users',
element: <div>Users List</div>,
},
]);
function App() {
return <RouterProvider router={router} />;
}
コンポーネントでのactionの利用
action
の結果は、useActionData
フックを使ってコンポーネント内で受け取ることができます。また、フォーム送信にはReact Routerの<Form>
コンポーネントを使用します。
import { Form, useActionData, redirect } from 'react-router-dom';
function NewUserPage() {
const actionData = useActionData(); // actionからの戻り値を受け取ります
return (
<div>
<h1>新しいユーザーを作成</h1>
<Form method="post">
<p>
<label>名前: <input type="text" name="name" required /></label>
</p>
<p>
<label>メールアドレス: <input type="email" name="email" required /></label>
</p>
<button type="submit">作成</button>
</Form>
{actionData && actionData.message && <p>{actionData.message}</p>}
</div>
);
}
上記の例では、フォームが送信されるとcreateUserAction
が呼び出され、redirect
関数を使って/users
パスへ遷移します。redirect
はResponse
オブジェクトを返すため、action
関数の返り値として直接利用できます。
エラーハンドリングとバリデーション:堅牢なアプリケーションのために
実用的なアプリケーションでは、フォームの入力値検証(バリデーション)やエラーハンドリングが不可欠です。action
関数内でこれらの処理を行い、結果をコンポーネントに返すことができます。バリデーションエラーが発生した場合、redirect
ではなくjson
関数を使ってエラー情報を返すのが一般的です。
import { Form, useActionData, json } from 'react-router-dom';
async function validateAndCreateUserAction({ request }) {
const formData = await request.formData();
const name = formData.get('name');
const email = formData.get('email');
const errors = {};
if (typeof name !== 'string' || name.length < 3) {
errors.name = '名前は3文字以上で入力してください。';
}
if (typeof email !== 'string' || !email.includes('@')) {
errors.email = '有効なメールアドレスを入力してください。';
}
// エラーがあればJSON形式で返す
if (Object.keys(errors).length) {
return json({ errors }, { status: 400 });
}
// エラーがなければAPI処理を行い、成功時にリダイレクトなど
// ... ユーザー作成処理 ...
return json({ message: 'ユーザーが正常に作成されました。' });
}
function NewUserPageWithValidation() {
const actionData = useActionData(); // エラー情報を受け取る
return (
<div>
<h1>新しいユーザーを作成 (バリデーション付き)</h1>
<Form method="post">
<p>
<label>名前: <input type="text" name="name" required /></label>
{actionData?.errors?.name && <p style={{ color: 'red' }}>{actionData.errors.name}</p>}
</p>
<p>
<label>メールアドレス: <input type="email" name="email" required /></label>
{actionData?.errors?.email && <p style={{ color: 'red' }}>{actionData.errors.email}</p>}
</p>
<button type="submit">作成</button>
</Form>
{actionData?.message && <p style={{ color: 'green' }}>{actionData.message}</p>}
</div>
);
}
// ルート定義でactionを割り当てます
// { path: '/users/new-validated', element: <NewUserPageWithValidation />, action: validateAndCreateUserAction }
actionとloaderの連携:データの一貫性を保つベストプラクティス
action
とloader
の連携は、React RouterのデータAPIの最も強力な機能の一つです。action
によってデータが変更されると、自動的にそのルートや関連するルートのloader
が再実行されます。これにより、UIに表示されているデータが常に最新の状態に保たれ、開発者が手動でデータの再取得ロジックを記述する必要がなくなります。
例えば、記事一覧ページで記事の削除action
を実行した場合、そのaction
が完了すると記事一覧のloader
が自動的に再実行され、削除された記事が反映された最新の一覧が取得・表示されます。これは、action
がデータ変更操作を、loader
がデータ取得操作を担当するという責務の分離を促進し、コードベースをクリーンに保つ上で非常に有効です。
補足: action
がエラー(例: ステータスコード400や500)を返した場合、デフォルトではloader
の再検証は行われません。これは、エラーレスポンスが必ずしもデータ変更を意味しないためです。必要に応じてshouldRevalidate
プロパティを使って再検証の挙動をカスタマイズできます。
UX向上に貢献するuseNavigationフック
フォーム送信のようなデータ変更操作は、完了までに時間がかかることがあります。この間、ユーザーに何もフィードバックがないと、アプリケーションがフリーズしたと誤解されたり、ボタンを何度もクリックしてしまったりと、ユーザー体験が悪化する可能性があります。useNavigation
フックは、このような「保留中のナビゲーション」の状態を管理し、より良いUXを提供するために役立ちます。
useNavigation
フックは、現在のナビゲーションの状態(idle
、submitting
、loading
)を提供します。
idle
: ナビゲーションやフォーム送信は行われていません。submitting
:<Form>
またはuseSubmit
によってPOST、PUT、PATCH、DELETEメソッドでフォームが送信中です。action
関数が実行されている状態です。loading
: 次のページのloader
が呼び出されており、データフェッチ中です。
この状態を利用して、フォーム送信中にボタンを無効化したり、ローディングスピナーを表示したりすることができます。
import { Form, useNavigation } from 'react-router-dom';
function ProductForm() {
const navigation = useNavigation();
const isSubmitting = navigation.state === 'submitting';
return (
<Form method="post">
<p>
<label>商品名: <input type="text" name="productName" required /></label>
</p>
<p>
<label>価格: <input type="number" name="price" required /></label>
</p>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? '追加中...' : '商品を追加'}
</button>
{isSubmitting && <p>商品をデータベースに保存しています...</p>}
</Form>
);
}
この例では、isSubmitting
がtrue
の間、ボタンが「追加中...」というテキストになり、無効化されます。これにより、ユーザーはフォームが処理中であることを視覚的に理解でき、重複送信を防ぐことができます。これは、React Routerが提供するプログレッシブエンハンスメントの恩恵であり、JavaScriptが無効な環境でも基本的なフォーム機能は動作しつつ、JavaScriptが有効な環境ではよりリッチなユーザー体験が提供されます。
まとめと今後の展望:React Router actionを使いこなす
React Routerのaction
機能は、モダンなWebアプリケーション開発におけるデータ変更処理のパラダイムを大きく変えるものです。ルーティング層でデータミューテーションを扱うことで、コンポーネントの責務を明確にし、コードの可読性と保守性を向上させます。また、loader
との自動的な連携によるデータ再検証は、開発者がデータの同期に頭を悩ませる時間を減らし、より本質的な機能開発に集中できるようになります。
本記事では、action
の基本的な使い方から、エラーハンドリング、バリデーション、そしてuseNavigation
フックを活用したUX改善まで、実務で役立つ具体的なコーディングプラクティスを紹介しました。これらの知識を深めることで、より堅牢でユーザーフレンドリーなReactアプリケーションを構築できるはずです。
今後、React RouterはReact 19やサーバーコンポーネントといった新しいWeb技術の進化と連携しながら、さらに強力なデータ管理機能を提供していくことが予想されます。これらの変化に常にアンテナを張り、最新のベストプラクティスを取り入れていくことが、システムエンジニアとして成長していく鍵となるでしょう。公式ドキュメント(React Router Official Documentation)を定期的にチェックし、最新情報をキャッチアップすることをお勧めします。
