はじめに
Reactアプリケーションでページ遷移を行う際、遷移先のページにデータを渡したい場面があります。例えば、一覧画面から詳細画面へ移動する際に選択したアイテムの情報を渡したり、フォーム送信後の完了画面に入力内容を表示したりするケースです。
React Routerでは、ルーティング時に状態を渡す方法として以下の2つが用意されています。
- Linkコンポーネントのstate属性を使う方法
- useNavigateフックのnavigate関数を使う方法
この記事では、それぞれの実装方法と使い分けの基準について解説します。
Link+stateで状態を渡す方法
Linkコンポーネントとは
Linkコンポーネントは、React Routerが提供する宣言的なナビゲーション用のコンポーネントです。HTML の <a> タグと同じように使用でき、クリックによるページ遷移を実現します。
基本的な実装方法
Linkコンポーネントの state 属性にオブジェクトを渡すことで、遷移先のページに状態を引き渡すことができます。
コード例
import { Link } from 'react-router-dom';
function UserList() {
const users = [
{ id: 1, name: '田中太郎', email: 'tanaka@example.com' },
{ id: 2, name: '佐藤花子', email: 'sato@example.com' }
];
return (
<div>
<h1>ユーザー一覧</h1>
{users.map(user => (
<div key={user.id}>
<Link
to={`/users/${user.id}`}
state={{ user: user }}
>
{user.name}
</Link>
</div>
))}
</div>
);
}
状態の受け取り方
遷移先では、useLocation フックを使って状態を取得します。
import { useLocation } from 'react-router-dom';
function UserDetail() {
const location = useLocation();
const user = location.state?.user;
if (!user) {
return <div>ユーザー情報が見つかりません</div>;
}
return (
<div>
<h1>ユーザー詳細</h1>
<p>名前: {user.name}</p>
<p>メール: {user.email}</p>
</div>
);
}
navigate+stateで状態を渡す方法
useNavigateフックとは
useNavigateフックは、プログラム的にページ遷移を行うための関数を返すフックです。ボタンのクリックイベントや非同期処理の完了後など、条件に応じた遷移を実装する際に使用します。
基本的な実装方法
navigate関数の第2引数に { state: { ... } } オブジェクトを渡すことで、状態を引き渡すことができます。
コード例
import { useNavigate } from 'react-router-dom';
import { useState } from 'react';
function UserForm() {
const navigate = useNavigate();
const [formData, setFormData] = useState({
name: '',
email: ''
});
const handleSubmit = async (e) => {
e.preventDefault();
// フォーム送信処理
const response = await submitUserData(formData);
if (response.success) {
// 送信成功時、完了画面へ状態を渡して遷移
navigate('/complete', {
state: {
userName: formData.name,
submittedAt: new Date().toISOString()
}
});
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
placeholder="名前"
/>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
placeholder="メールアドレス"
/>
<button type="submit">送信</button>
</form>
);
}
状態の受け取り方
Link+stateの場合と同様に、useLocation フックで状態を取得します。
import { useLocation, Link } from 'react-router-dom';
function CompletePage() {
const location = useLocation();
const { userName, submittedAt } = location.state || {};
return (
<div>
<h1>送信完了</h1>
<p>{userName}さん、ありがとうございました</p>
<p>送信日時: {new Date(submittedAt).toLocaleString()}</p>
<Link to="/">トップへ戻る</Link>
</div>
);
}
2つの方法の使い分け
Link+stateとnavigate+stateは、どちらも同じように状態を渡すことができますが、使用場面によって適切な方法が異なります。
特徴の比較表
| 項目 | Link+state | navigate+state |
|---|---|---|
| 実装方法 | JSX内に宣言的に記述 | 関数内で命令的に実行 |
| 主な用途 | ユーザーのクリック操作による遷移 | 条件分岐や非同期処理後の遷移 |
| 記述場所 | JSXのreturn文内 | イベントハンドラや useEffect 内 |
| 適したケース | リンクやメニュー項目 | フォーム送信、API呼び出し後の遷移 |
| 可読性 | 遷移先が視覚的に明確 | 処理のフローに沿った記述 |
Link+stateを使うべきケース
以下のような場面では、Linkコンポーネントの使用が適しています。
- 一覧から詳細への遷移: ユーザーリスト、商品リストなどでアイテムをクリックした際の遷移
- ナビゲーションメニュー: ヘッダーやサイドバーのリンク
- 静的なリンク: 特定の条件判定なしに常に表示されるリンク
// 一覧画面の例
<Link to="/product/123" state={{ product: productData }}>
商品を見る
</Link>
navigate+stateを使うべきケース
以下のような場面では、navigateフックの使用が適しています。
- フォーム送信後の遷移: バリデーションや送信処理が成功した後の画面遷移
- 認証後のリダイレクト: ログイン成功後に元のページへ戻る処理
- 条件分岐による遷移: 特定の状態や権限に応じて遷移先を変更する場合
- タイマーや非同期処理後の遷移: 一定時間後や API レスポンス後の自動遷移
// フォーム送信の例
const handleSubmit = async (data) => {
const result = await validateAndSubmit(data);
if (result.success) {
navigate('/success', { state: { result } });
}
};
注意点とベストプラクティス
状態が失われるケース
ルーティングで渡した状態は、以下の場合に失われます。
- ページをリロードした場合: ブラウザのリロード操作で状態は消失します
- URLを直接入力してアクセスした場合: 状態は遷移元から渡されるため、直接アクセスでは取得できません
- ブラウザの戻る/進むボタン: 基本的には保持されますが、セッションストレージとの併用が推奨されます
そのため、状態が存在しない場合の処理を必ず実装しておく必要があります。
function DetailPage() {
const location = useLocation();
const data = location.state?.data;
// 状態が存在しない場合の処理
if (!data) {
return <Navigate to="/" replace />;
}
return <div>{/* 通常の表示 */}</div>;
}
セキュリティ上の考慮事項
ルーティングで渡される状態は、ブラウザの開発者ツールから確認できます。そのため、以下の情報を含めないようにしましょう。
- パスワードやトークンなどの認証情報
- 個人情報や機密データ
- サーバー側で検証が必要な重要なデータ
重要な情報は必ずサーバー側で管理し、必要に応じて API 経由で取得する設計にします。
代替手段の検討
状態の受け渡しが複雑になる場合や、複数のコンポーネント間で共有が必要な場合は、以下の代替手段も検討しましょう。
URL パラメータやクエリ文字列
永続性が必要で、ブックマークや共有が必要な情報はURLで渡します。
// URLパラメータの例
<Link to={`/user/${userId}?tab=profile`}>
プロフィールを見る
</Link>
グローバル状態管理
複数のページで同じ状態を参照する場合は、Context API や状態管理ライブラリを使用します。
// Context APIの例
const UserContext = createContext();
function App() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
<Routes>
{/* ルート定義 */}
</Routes>
</UserContext.Provider>
);
}
ローカルストレージやセッションストレージ
リロード後も状態を保持したい場合は、ブラウザのストレージ機能を活用します。
まとめ
React Routerで状態を渡す方法として、Link+stateとnavigate+stateの2つのアプローチを紹介しました。
- Link+state: 宣言的で可読性が高く、ユーザーのクリック操作による遷移に適している
- navigate+state: プログラム的な制御が可能で、条件分岐や非同期処理後の遷移に適している
どちらの方法も useLocation フックで同じように状態を取得できますが、使用場面に応じて適切な方を選ぶことで、より保守性の高いコードを書くことができます。状態が失われるケースやセキュリティ面にも注意しながら、適切に実装していきましょう。