React Router Link を使ってみよう!分かりやすい解説

はい、承知いたしました。React RouterのLinkコンポーネントに焦点を当て、詳細な解説を含む約5000語の記事を作成します。


React Router Link を使ってみよう!SPAの要、分かりやすい解説

Webアプリケーション開発において、ページ間の遷移はユーザー体験の根幹をなす要素です。ユーザーがアプリケーション内をスムーズに移動できるかどうかは、そのアプリケーションの使いやすさを大きく左右します。従来のWebサイトでは、ページ遷移はブラウザがサーバーにリクエストを送り、新しいHTMLページを完全に読み込み直すという仕組みが一般的でした。これはシンプルで確実な方法ですが、遷移ごとに画面全体が再描画されるため、一瞬の空白時間が発生したり、アプリケーション全体が一貫性を失ったりといった課題がありました。

現代のWeb開発、特にSingle Page Application (SPA) の台頭により、このページ遷移の概念は大きく変わりました。SPAでは、初期読み込み時にアプリケーションの全体(または大部分)が読み込まれ、その後の画面遷移はブラウザがサーバーにフルリクエストを送るのではなく、JavaScriptがブラウザの表示内容を動的に書き換えることで実現されます。これにより、ページ遷移が高速になり、ユーザーはデスクトップアプリケーションに近い滑らかな操作感を得ることができます。

しかし、SPAでどのようにしてブラウザの履歴管理(「戻る」「進む」ボタンの機能)や、特定のURLへの直接アクセス(例:/about というURLを直接入力してそのページを表示する)を実現するのでしょうか?ここで必要になるのが、「クライアントサイドルーティング」という技術です。

Reactにおけるクライアントサイドルーティングのデファクトスタンダードとも言えるライブラリが、「React Router」です。React Routerは、宣言的な方法でアプリケーションのルーティング(URLと表示するコンポーネントのマッピング)を定義することを可能にします。そして、このReact Routerの機能の中でも、ユーザーがアプリケーション内を移動するための主要な手段を提供するのが、今回焦点を当てるLinkコンポーネントです。

Linkコンポーネントは、従来のHTMLの<a>タグのような見た目や機能を提供しますが、その内部の挙動は大きく異なります。<a>タグがデフォルトでブラウザにフルリロードを伴う新しいページへの遷移を指示するのに対し、LinkコンポーネントはReact Routerと連携し、SPAの枠組みの中で、ブラウザの履歴APIを利用してURLを変更し、対応するコンポーネントを再描画するというスマートな遷移を実現します。

この記事では、React RouterのLinkコンポーネントの基本的な使い方から、その重要なProps、さらに高度な使い方や、他のナビゲーション手段(<a>タグ、NavLinkuseNavigate)との違いについて、約5000語の詳細な解説を通して深く掘り下げていきます。この記事を読み終える頃には、React RouterのLinkコンポーネントを自信を持って使いこなし、より快適で効率的なSPA開発ができるようになっているでしょう。

さあ、React RouterのLinkコンポーネントの世界へ飛び込んでみましょう。

Webにおけるページ遷移の基礎:<a>タグとフルリロード

React RouterのLinkコンポーネントの重要性を理解するためには、まず伝統的なWebにおけるページ遷移の仕組みを理解しておくことが役立ちます。

最も基本的で歴史のあるページ遷移の方法は、HTMLの<a>タグを使用することです。<a>タグはアンカー要素と呼ばれ、href属性に指定されたURLへハイパーリンクを張るために使われます。

html
<a href="/about">About Us</a>

ユーザーがこのリンクをクリックすると、ブラウザはhref属性に指定されたURLに対して新しいHTTPリクエストをサーバーに送信します。サーバーはそのリクエストを受け取り、対応するHTMLファイルを生成(または取得)してブラウザに返します。ブラウザはそのHTMLファイルを解析し、CSSやJavaScriptなどの関連リソースを読み込み、ページ全体をゼロから再描画します。このプロセスは「フルリロード」と呼ばれます。

フルリロードはシンプルで理解しやすい仕組みですが、いくつかのデメリットがあります。

  1. 遅延: サーバーへのリクエスト、サーバーでの処理、レスポンスの送信、ブラウザでの再描画という一連のプロセスには時間がかかります。特にネットワーク環境が悪い場合や、サーバーの負荷が高い場合には、ユーザーは次のページが表示されるまでの待ち時間を長く感じることになります。
  2. ユーザー体験の中断: ページ全体が再描画されるため、画面が一瞬真っ白になったり、要素がガタついたりすることがあります。これはアプリケーションの一貫性を損ない、ユーザーに不快感を与える可能性があります。
  3. 状態の喪失: 現在のページのJavaScriptによる状態(例:モーダルの表示状態、フォームの入力内容、アニメーションの進行度など)は、フルリロードによってすべてリセットされてしまいます。

クライアントサイドルーティングとSPA

これらのフルリロードのデメリットを克服するために登場したのが、Single Page Application (SPA) とクライアントサイドルーティングです。SPAでは、アプリケーションの起動時に必要なリソースをまとめて(あるいは分割して少しずつ)読み込み、その後はサーバーとのやり取りを最小限に抑えます。

SPAにおけるページ遷移は、ブラウザのJavaScriptが担当します。ユーザーがリンクをクリックしたり、特定の操作を行ったりした際に、JavaScriptが新しいコンテンツを取得(API通信など)し、DOMを動的に操作して画面の一部または全体を書き換えます。このとき、ページ全体を再読み込みするのではなく、必要な部分だけが更新されます。

しかし、画面の内容だけをJavaScriptで書き換えるだけでは不都合が生じます。

  • URLが変わらない: 画面内容は変わったのに、ブラウザのアドレスバーに表示されているURLが最初のページのままになってしまいます。これでは、ユーザーは現在どのページにいるのか分からなくなりますし、特定のページへのブックマークや共有ができなくなります。
  • 「戻る」「進む」ボタンが機能しない: ブラウザの履歴に新しいエントリが追加されないため、ユーザーがブラウザの「戻る」ボタンをクリックしても、期待通りに前の画面に戻ることができません。

これらの問題を解決するために、SPAでは「History API」というブラウザの機能を利用します。History APIを使用すると、JavaScriptからブラウザのアドレスバーのURLを変更したり、履歴スタックに新しいエントリを追加したり、履歴スタック内のエントリを置き換えたりすることができます。これにより、画面の表示内容とブラウザのURL・履歴を同期させることが可能になります。

クライアントサイドルーティングライブラリ(React Routerなど)は、このHistory APIを抽象化し、開発者が宣言的かつ効率的にSPAのルーティングを管理できるようにするツールです。そして、Linkコンポーネントは、このクライアントサイドルーティングの仕組みを利用して、SPA内でのナビゲーションを実現するためのインターフェースを提供するのです。

React Routerとは

React Routerは、Reactアプリケーションにおける宣言的なルーティングのための標準的なライブラリです。Reactのコンポーネントベースのパラダイムと非常に相性が良く、ルーティングの設定もコンポーネントとして扱うことができます。

React Routerの主な役割は以下の通りです。

  1. URLとコンポーネントのマッピング: 特定のURLパス(例:/about, /users/:id)が要求されたときに、どのReactコンポーネントを表示するかを定義します。
  2. ナビゲーションの管理: ユーザーがアプリケーション内を移動するための手段を提供します(例:Linkコンポーネント、useNavigateフック)。これらの手段は、ブラウザのHistory APIを利用してURLを変更し、React Routerにルーティングの更新を通知します。
  3. ルーティング情報の提供: 現在のURL、パスパラメータ、クエリストリング、履歴の状態などを、表示されているコンポーネントからアクセスできるようにします(例:useParams, useLocation, useMatch フック)。

React Routerの主要なコンポーネント/フックには以下のようなものがあります。

  • BrowserRouter: ブラウザのHistory APIを使用したルーティングの基盤となるコンポーネント。アプリケーション全体をこれで囲みます。
  • Routes: ルーティングルールを定義するコンポーネント群をまとめるコンテナ。
  • Route: 特定のパスと、そのパスにマッチしたときに表示するコンポーネントをマッピングするコンポーネント。
  • Link: SPA内で別のルートへ遷移するためのナビゲーションリンクを生成するコンポーネント。
  • NavLink: Linkとほぼ同じですが、現在のURLにマッチしている場合に特別なスタイルを適用できるコンポーネント。
  • useNavigate: プログラムによるナビゲーション(ユーザー操作以外での遷移、ボタンクリック時の遷移など)を行うためのフック。
  • useLocation: 現在のURL情報(パス名、クエリストリング、ハッシュ、stateなど)を取得するためのフック。
  • useParams: URLのパスパラメータを取得するためのフック。

これらのコンポーネントやフックを組み合わせることで、複雑なルーティングを持つSPAも効率的に構築できます。そして、その中でもユーザーとのインタラクションの最前線に立ち、ナビゲーションの起点となるのが、今回学ぶLinkコンポーネントなのです。

Linkコンポーネントの基礎:SPAでのナビゲーションの要

Linkコンポーネントは、React Routerにおいて、ユーザーが別のページやルートへ遷移するための主要なUI要素です。見た目は一般的なHTMLの<a>タグと同じように表示されますが、クリックされたときの挙動が異なります。

前述の通り、HTMLの<a>タグはデフォルトでブラウザに指定されたURLへのフルリロードを指示します。一方、Linkコンポーネントは、クリックされるとイベントを横取り(preventDefault()のようなことを内部で行う)し、React Routerに対して指定されたパスへの遷移を指示します。React Routerは、History APIを使用してブラウザのアドレスバーのURLを変更し、履歴スタックを更新し、その後、新しいURLに対応するコンポーネントをレンダリングします。このプロセス全体がJavaScriptの制御下で行われるため、ページ全体のフルリロードは発生しません。

<a>タグとの比較

特徴 <a>タグ Linkコンポーネント (React Router)
遷移方法 フルリロード(サーバーへのHTTPリクエスト) クライアントサイドルーティング (History API)
画面の挙動 ページ全体が再描画される DOMの一部が書き換えられる(SPAのまま)
URLの扱い 指定されたURLへ直接リクエスト React Routerが管理する内部ルートへ遷移
履歴管理 ブラウザのデフォルト履歴機能 History APIを利用した履歴管理
状態の保持 フルリロードでリセットされる アプリケーションの状態は維持される(SPA)
用途 外部サイトへのリンク、ファイルダウンロード SPA内部のルート間遷移
必須ライブラリ なし React Router

このように、LinkコンポーネントはSPA内部でのスムーズな遷移を実現するために設計されています。外部サイトへのリンクなど、明示的にフルリロードが必要な場合を除き、React Routerを使用しているアプリケーション内でのナビゲーションにはLinkコンポーネントを使用するのが基本です。

基本的な使い方

Linkコンポーネントの最も基本的な使い方は、遷移先のパスをtoというPropsに指定することです。

まず、React Routerをインストールしていない場合はインストールします。

“`bash
npm install react-router-dom

または

yarn add react-router-dom
“`

アプリケーションのルートコンポーネント(通常はApp.jsindex.jsなど)をBrowserRouterで囲みます。

“`jsx
// index.js または App.js
import React from ‘react’;
import ReactDOM from ‘react-dom/client’;
import { BrowserRouter } from ‘react-router-dom’;
import App from ‘./App’;

const root = ReactDOM.createRoot(document.getElementById(‘root’));
root.render(





);
“`

次に、ルーティングを定義します。

“`jsx
// App.js
import React from ‘react’;
import { Routes, Route, Link } from ‘react-router-dom’;

function Home() {
return

Home Page

;
}

function About() {
return

About Page

;
}

function Contact() {
return

Contact Page

;
}

function App() {
return (

  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    <Route path="/contact" element={<Contact />} />
  </Routes>
</div>

);
}

export default App;
“`

この例では、ナビゲーションバーに3つのLinkコンポーネントがあります。

  • <Link to="/">Home</Link>: / パスへのリンクです。クリックすると、Homeコンポーネントが表示されます。
  • <Link to="/about">About</Link>: /about パスへのリンクです。クリックすると、Aboutコンポーネントが表示されます。
  • <Link to="/contact">Contact</Link>: /contact パスへのリンクです。クリックすると、Contactコンポーネントが表示されます。

これらのLinkをクリックしても、ページ全体がリロードされることはありません。ブラウザのアドレスバーのURLが変わり、それに応じて<Routes>内で定義されたRouteがマッチし、対応するコンポーネントがレンダリングされます。

Linkコンポーネントの主要なProps

Linkコンポーネントは、基本的なtoProps以外にも、様々なカスタマイズや高度な機能を提供するPropsを持っています。ここでは、主要なPropsについて詳しく見ていきましょう。

to (必須)

遷移先のパスを指定するPropsです。最も基本的で重要なPropsであり、省略することはできません。toには文字列またはオブジェクトを指定できます。

1. 文字列による指定:

最も一般的な方法です。絶対パスまたは相対パスを指定できます。

  • 絶対パス: /で始まるパスです。アプリケーションのルートからの絶対位置を示します。

    jsx
    <Link to="/">トップページへ</Link>
    <Link to="/users/123">ユーザー詳細へ</Link>
    <Link to="/products?category=electronics">製品一覧へ (クエリ付き)</Link>

  • 相対パス: /で始まらないパスです。現在のURLからの相対位置を示します。

    例えば、現在のURLが /users/profile で、<Link to="edit">編集</Link> とした場合、遷移先は /users/edit となります。また、<Link to="../">親ディレクトリへ</Link> とした場合は、/users へ遷移します。相対パスはネストされたルーティングで特に便利です。後ほど詳細を説明します。

    jsx
    // 例: 現在のURLが /users/123 の場合
    <Link to="edit">このユーザーを編集</Link> {/* -> /users/123/edit */}
    <Link to="../">ユーザー一覧へ</Link> {/* -> /users */}
    <Link to="../../">トップへ</Link> {/* -> / */}

2. オブジェクトによる指定:

より複雑なURL(クエリストリング、ハッシュフラグメント)や、遷移先に情報を渡したい場合に使用します。toPropsに以下のプロパティを持つオブジェクトを指定します。

  • pathname: 遷移先のパス名(文字列)。/users/123 など。
  • search: クエリストリング(文字列)。?category=electronics&sort=price など。先頭の?を含める必要があります。
  • hash: ハッシュフラグメント(文字列)。#section-title など。先頭の#を含める必要があります。
  • state: 遷移先のルートで利用できる状態(任意の値)。オブジェクトなどを渡せます。

    “`jsx
    <Link
    to={{
    pathname: ‘/products’,
    search: ‘?category=electronics&sort=price’,
    hash: ‘#description’,
    state: { fromProductList: true, page: 1 } // stateを渡す
    }}

    製品一覧へ (オブジェクト形式)

    “`

    遷移先 (/products にマッチするコンポーネント) では、useLocationフックを使ってこれらの情報(pathname, search, hash, state)を取得できます。

    “`jsx
    import { useLocation } from ‘react-router-dom’;

    function ProductList() {
    const location = useLocation();
    console.log(location.pathname); // ‘/products’
    console.log(location.search); // ‘?category=electronics&sort=price’
    console.log(location.hash); // ‘#description’
    console.log(location.state); // { fromProductList: true, page: 1 }

    // … レンダリングロジック …
    }
    “`

    stateは、ページ遷移時に一時的な情報(例: モーダルを開いていたか、スクロール位置、前のページからのデータなど)を渡すのに非常に便利です。ブラウザのリロード時には失われる情報であることに注意してください。

replace (boolean)

このPropsをtrueに設定すると、クリックによる遷移が、ブラウザの履歴スタックに新しいエントリを追加するのではなく、現在の履歴エントリを置き換えます。デフォルトはfalseで、新しいエントリが追加されます。

履歴スタックの置き換えは、特定のシナリオで役立ちます。例えば、ユーザーがログインした後にホームページにリダイレクトする場合、ログインページに戻れないようにするためにreplace={true}を使用することがよくあります。

jsx
// ログイン成功後のリダイレクトリンク(例)
// 通常はプログラムによる遷移 (useNavigate) で行うことが多いですが、Linkでも可能
<Link to="/dashboard" replace={true}>ダッシュボードへ</Link>

ユーザーがこのリンクをクリックすると、履歴スタックは [..., ログインページ] から [..., ダッシュボードページ] のように変わります。これにより、ユーザーは「戻る」ボタンでログインページに戻ることはできません。

state (any)

これはtoPropsのオブジェクト形式で指定できるstateと同じ機能です。LinkコンポーネントのPropsとして直接指定することもできます。どちらの書き方でも結果は同じです。

jsx
// to={{ pathname: '/some-path', state: { data: '...' } }} と同じ
<Link to="/some-path" state={{ data: 'some value' }}>
状態を渡して遷移
</Link>

state Propsは、toPropsが文字列で指定されている場合でも使用できます。

jsx
<Link to="/users/456" state={{ fromList: true }}>
ユーザー456の詳細へ
</Link>

relative (‘route’ | ‘path’) (v6.4+)

このPropsは、toPropsに相対パス(/で始まらないパス)を指定した場合の基準点を変更します。React Router v6.4で導入された機能です。

  • relative="path" (デフォルト): toに指定された相対パスは、現在のURLパスを基準とします。例えば、現在のURLが /users/123to="../" と指定した場合、これは現在のパス /users/123 の「親ディレクトリ」という意味になり、結果として /users に遷移します。
  • relative="route": toに指定された相対パスは、現在アクティブになっているRouteコンポーネントのパスを基準とします。これは、ネストされたルーティングで特に便利です。

例を見てみましょう。以下のようなルーティングとコンポーネントがあるとします。

“`jsx
// App.js
import { Routes, Route, Link } from ‘react-router-dom’;
import Layout from ‘./Layout’;
import Users from ‘./Users’;
import UserDetail from ‘./UserDetail’;

function App() {
return (

}>
}> {/ ネストされたルート /}
} /> {/ さらにネスト /}



);
}
// Layout.js (簡単なレイアウトコンポーネント)
import { Link, Outlet } from ‘react-router-dom’;

function Layout() {
return (

My App

{/ 子ルートの要素が表示される場所 /}

);
}
// Users.js (ユーザー一覧)
import { Link, Outlet } from ‘react-router-dom’;

function Users() {
const users = [{ id: 1, name: ‘Alice’ }, { id: 2, name: ‘Bob’ }];
return (

ユーザー一覧

    {users.map(user => (

  • {user.name} {/ 相対パス: ‘1’, ‘2’ /}
  • ))}

{/ /users/:userId にマッチしたUserDetailが表示される /}

);
}
// UserDetail.js (ユーザー詳細)
import { useParams, Link } from ‘react-router-dom’;

function UserDetail() {
const { userId } = useParams();

return (

ユーザー詳細: {userId}

{/ ここで Link to=”../” を使う場合 /}
{/ ユーザー一覧に戻る /}
{/ ユーザー一覧に戻る (route相対) /}

);
}
“`

ユーザーが /users/1 のページ(UserDetailが表示されている状態)にいるとします。

  • relative="path" (デフォルト): 現在のURLは /users/1 です。to="../" は現在のURLのパス /users/1 の親ディレクトリ、つまり /users を指します。<Link to="../">/users へ遷移します。これは期待通りの挙動かもしれません。

  • relative="route": 現在アクティブな最も内側のRoute<Route path=":userId" element={<UserDetail />} /> です。その親のRoute<Route path="users" element={<Users />}> です。<Link to="../" relative="route"> は、この親のRouteのパス /users を基準として、その親(/)を指します。つまり / へ遷移します。これは多くの場合、期待する挙動ではないかもしれません。

では、relative="route" はどのような場合に役立つのでしょうか?ネストされたルーティングのコンテキストで、特定の親ルートに戻りたい場合などです。

例えば、/users/1/edit のようなルートがあり、編集ページからユーザー詳細 /users/1 に戻るリンクを作りたいとします。

“`jsx
// UserEdit.js (ユーザー編集)
import { useParams, Link } from ‘react-router-dom’;

function UserEdit() {
const { userId } = useParams();

return (

ユーザー編集: {userId}

{/*
現在のURLが /users/1/edit の場合:
to=”../” (relative=”path”): /users/1 に遷移 (OK)
to=”../” relative=”route”: このルートは /users/:userId/edit となるはずなので、その親、つまり /users/:userId つまり /users/1 に遷移 (OK)

    ... あれ?同じ挙動になるのでは?
    そうです、単純な ../ の場合は同じになりがちです。
    relative="route" は、Linkがレンダリングされているルートの"定義パス"からの相対パスを解決します。
    例えば、LinkがUserEditコンポーネント内にあり、UserEditが <Route path="edit" element={<UserEdit />} /> にマッチしている場合、
    relative="route" の場合の基準は、その親 Route のパス、つまり /users/:userId となります。
    to="../" は、この /users/:userId から見て一つ上の階層、つまり /users を指します。
    したがって、URLが /users/1/edit の場合、Link to="../" relative="route"> は /users に遷移します。
  */}

  {/* ユーザー詳細に戻るなら /users/1 へ明示的に飛ぶか、
     relative="path" (デフォルト) で to="../" を使うのが分かりやすい
     Link to={`/users/${userId}`}>ユーザー詳細に戻る</Link>
     または
     <Link to="../">ユーザー詳細に戻る</Link> // relative="path" がデフォルト
  */}

  {/* relative="route" の例として挙げられるのは、特定の親ルート内のサブページへのリンクを、
     その親ルートのコンポーネント内から相対的に張る場合などです。
     例えば、ユーザー詳細ページ (/users/:userId) 内で、
     「コメント (/users/:userId/comments)」や「アクティビティ (/users/:userId/activity)」へのリンクを作る場合。
     UserDetailコンポーネントは /users/:userId ルートにマッチしています。
  */}
  {/* UserDetail.js 内での例 */}
  {/* URLが /users/1 の場合: */}
  {/* <Link to="comments">コメントを見る</Link> // relative="path" -> /users/1/comments (OK) */}
  {/* <Link to="comments" relative="route">コメントを見る</Link> // relative="route" -> /users/1/comments (OK) */}

  {/* 違いが出やすいのは、ルート定義のパスと現在のURLの階層が異なる場合など。
     例えば、/users/:userId/edit にマッチするコンポーネント内で、
     そのコンポーネントをレンダリングしている Route の定義パスからの相対で戻りたい場合。
     <Route path="edit" element={<UserEdit />} /> は /users/:userId の子ルート。
     UserEditコンポーネント内で <Link to="../" relative="route"> とすると、
     これは /users/:userId から見て ../ なので /users に遷移します。
  */}
  {/* この例では、/users/1/edit から /users に戻るリンクになる */}
  <Link to="../" relative="route">ユーザー一覧に戻る (relative="route")</Link>

  {/* 一方、<Link to="../"> (relative="path") は、/users/1/edit から見て ../ なので /users/1 に遷移 */}
  <Link to="../">ユーザー詳細に戻る (relative="path")</Link>
</div>

);
}
``relative=”route”は、Linkが配置されているコンポーネントがマッチしているRouteコンポーネントのパス定義(例:path=”:userId”`)からの相対で解決したい場合に利用します。これにより、現在のURLの実際のセグメント数に関わらず、定義上の親ルートへの相対リンクなどを書きやすくなります。

少し理解が難しい機能ですが、ネストされたルーティングを多用する際に、意図した通りの相対パス解決を行うために覚えておくと役立ちます。多くの場合、デフォルトの relative="path" で十分です。

preventScrollReset (boolean) (v6.4+)

このPropsをtrueに設定すると、ナビゲーション時に自動的に行われるウィンドウのスクロール位置リセットを防ぎます。デフォルトはfalseで、新しいページに遷移した際にウィンドウのスクロール位置が最上部(0,0)にリセットされます。

SPAでは、画面の一部分だけが更新されることが多いため、ページ遷移してもスクロール位置が維持されると、ユーザーが混乱したり、コンテンツを見逃したりする可能性があります。そのため、デフォルトでスクロール位置をリセットするのは妥当な挙動です。

しかし、特定のケースではスクロール位置を維持したい場合があります。例えば、長いリストを表示していて、そのリスト内の要素をクリックして詳細ページに遷移し、その後ブラウザの「戻る」ボタンや、リストページに戻るリンクで戻ってきた際に、詳細を見る前にスクロールしていた位置に戻ってほしい、といった場合です。

このようなシナリオでは、preventScrollReset={true} を設定することで、Linkクリック時の自動スクロールリセットを防ぐことができます。ただし、これはそのLinkをクリックした場合に限られます。ブラウザの「戻る/進む」ボタンでの遷移や、useNavigateを使った遷移には影響しません。React Router v6.4からは、スクロール位置の復元はScrollRestorationコンポーネントや、RouterProviderなどの新しいAPIでより柔軟に制御できるようになっています。preventScrollResetは、個別のLinkでの挙動を調整するためのものです。

jsx
<Link to="/long-list" preventScrollReset={true}>
長いリストページに戻る (スクロール位置維持)
</Link>

その他のProps

Linkコンポーネントは、HTMLの<a>タグが受け付ける多くの標準的なHTML属性もPropsとして受け付けます。これらは、生成される<a>要素にそのまま適用されます。

  • className: CSSクラスを指定します。スタイリングに利用します。
  • style: インラインスタイルを指定します。
  • id: 要素のIDを指定します。
  • title: ツールチップとして表示されるテキストを指定します。
  • onClick: クリックイベントハンドラを指定します。Linkによるナビゲーションの前に実行される処理を記述できます。後述。
  • target: リンクを開くウィンドウを指定します (_blankで新しいタブなど)。ただし、_blank以外を指定すると、内部的には<a>タグのデフォルトの挙動(フルリロード)になる可能性があるため、外部サイトへのリンクなど、明示的にフルリロードが必要な場合にのみ使用するのが安全です。SPA内部リンクで新しいタブを開きたい場合は、target="_blank"rel="noopener noreferrer"をセットで使い、同時にonClickevent.preventDefault()してReact Routerによる遷移を防ぐ、という工夫が必要になる場合があります(または単に通常の<a>タグを使う)。

“`jsx
console.log(‘Settings link clicked’)}

Settings

“`

これらのPropsを使うことで、Linkコンポーネントの見た目や基本的な挙動をカスタマイズできます。

to Propの詳細な使い方:パスの指定方法

to PropはLinkコンポーネントの核となる部分です。文字列とオブジェクト、そして相対パスの使い分けについて、もう少し詳しく見ていきましょう。

文字列による指定

最もシンプルで直感的な方法です。

  • 絶対パス:

    • /: アプリケーションのルートパス。
    • /about: /about という正確なパス。
    • /users/123: /users/123 というパス。パスパラメータを含むルート (<Route path="/users/:userId">) にマッチします。
    • /products?category=electronics#description: クエリストリングとハッシュフラグメントを含むパス。

    文字列でクエリストリングやハッシュを指定する場合、これらは単なる文字列の一部として扱われます。遷移先のコンポーネントでこれらの情報(?category=...#...)を取得するには、useLocationフックを使用する必要があります。

  • 相対パス:

    • /で始まらないパス。現在のURLを基準とします(relative="path"の場合)。
    • ./: 現在のパス自体。
    • ..: 現在のパスの親ディレクトリ。
    • ../..: 現在のパスから2階層上のディレクトリ。
    • edit: 現在のパスに /edit を加えたパス。例: /users/123 -> /users/123/edit

相対パスは、特にネストされたルーティング構造を持つアプリケーションで便利です。コンポーネントがどの親ルートの下にレンダリングされているかを意識せずに、相対的なリンクを張ることができます。ただし、意図しないパスに遷移しないように、現在のルーティング構造と相対パスの解決ルールを正確に理解しておく必要があります。

オブジェクトによる指定

より構造化された方法で遷移先を指定できます。パス、クエリストリング、ハッシュ、そして状態を明示的に分離して指定します。

“`jsx
<Link
to={{
pathname: ‘/search’,
search: ‘?query=react&page=1’,
hash: ‘#results’,
state: { from: ‘homepage’ }
}}

検索結果へ

“`

  • pathname: 必須ではありませんが、パスを指定する場合に使用します。省略すると現在のpathnameが使われます。
  • search: クエリストリングを指定します。URLSearchParamsオブジェクトを使うと、クエリストリングの構築が容易になります。

    ``jsx
    const params = new URLSearchParams({ query: 'react', page: '1' });
    <Link
    to={{
    pathname: '/search',
    search:
    ?${params.toString()}` // “?query=react&page=1” を生成
    }}

    検索結果へ (URLSearchParams)

    ``searchプロパティの値は文字列である必要があります。URLSearchParams`を使うとオブジェクトから文字列への変換が簡単に行えます。

  • hash: ハッシュフラグメントを指定します。ページ内の特定のセクションへのリンクに使用されます。SPAでは、ハッシュが変更されても通常はフルリロードは起きず、ブラウザがそのIDを持つ要素までスクロールします。

  • state: 遷移先に任意のデータを渡すことができます。これはURLの一部にはなりませんし、ブラウザのリロードや直接URL入力時には失われます。セッションストレージやローカルストレージを使わずに、一時的に状態を渡したい場合に便利です。例えば、リストページから詳細ページへ遷移する際に、リストページでのスクロール位置やフィルタリング条件をstateとして渡し、詳細ページから戻ってきたときにその状態を復元するために利用できます。

相対パスと絶対パスの使い分け

  • 絶対パス (/ で始まる):

    • アプリケーション内の明確な固定パスへのリンクに最適です。
    • ナビゲーションメニュー(ヘッダーやフッターなど)のように、アプリケーション内のどこからでも同じ場所へリンクしたい場合に主に使用します。
    • 現在のURLやコンポーネントの配置に依存しないため、予測しやすいです。
  • 相対パス (/ で始まらない):

    • ネストされたルーティング構造を持つアプリケーションで役立ちます。
    • 現在のコンポーネントやURLコンテキストからの相対的な位置へのリンクに使います。
    • 例: ユーザー詳細ページ /users/:userId の中で、そのユーザーに関連するサブページ(例: /users/:userId/posts)へのリンクを張る場合、./posts という相対パスを使えば、userId が何であっても正しく /users/:userId/posts に解決されます。
    • 親ルートに戻る (../) など、階層構造を移動する際に便利です。
    • relative Propsを使うことで、相対パスの基準点を変更できます(上級)。

どちらを使うべきかは、リンクの用途やアプリケーションのルーティング構造によって判断します。一般的には、グローバルなナビゲーションには絶対パスを、特定のセクションやリスト内のアイテムなど、現在のコンテキストに依存するリンクには相対パスを使うことが多いでしょう。

Linkコンポーネントの高度な使い方/考慮事項

基本的な使い方だけでなく、Linkコンポーネントにはいくつかの応用的な使い方や、開発時に考慮すべき点があります。

動的なパスの生成

パスパラメータを含むルート(例: /users/:userId)へのリンクを生成する場合、toPropsの値は動的になります。JavaScriptのテンプレートリテラルや文字列連結を使って、パス文字列を生成します。

jsx
// ユーザーIDをpropsで受け取るリストアイテムコンポーネント
function UserListItem({ userId, userName }) {
return (
<li>
<Link to={`/users/${userId}`}>{userName}</Link>
</li>
);
}

この例では、userIdという変数を使って遷移先のパスを動的に生成しています。これはSPAでよく見られるパターンです。

クエリストリングやハッシュフラグメントの扱い

前述の通り、toPropsのオブジェクト形式を使うことで、クエリストリング(search)やハッシュフラグメント(hash)を明確に指定できます。

“`jsx
// クエリストリング付きのリンク生成
<Link
to={{
pathname: ‘/products’,
search: new URLSearchParams({
category: ‘books’,
sort: ‘title’,
page: ‘2’
}).toString()
}}

書籍の2ページ目へ

“`

遷移先コンポーネントでは、useLocation().search でクエリストリング全体(?category=...)を取得し、それをURLSearchParamsコンストラクタに渡すことで、各クエリパラメータの値を取り出せます。

“`jsx
import { useLocation } from ‘react-router-dom’;

function ProductList() {
const location = useLocation();
const params = new URLSearchParams(location.search);
const category = params.get(‘category’); // ‘books’
const page = params.get(‘page’); // ‘2’

// … データ取得や表示ロジック …
}
“`

ハッシュフラグメントも同様にuseLocation().hashで取得できます。通常、ハッシュはページ内の要素にジャンプするために使われますが、JavaScript側で特別な処理をフックすることも可能です(例: window.addEventListener('hashchange', ...))。

onClick ハンドラの利用

Linkコンポーネントは、HTMLの<a>タグと同様にonClickPropsを受け付けます。このハンドラは、Linkによるナビゲーションが実行される前に呼び出されます。

これにより、以下のような処理を行うことができます。

  • 遷移前の確認: ユーザーに操作の確認ダイアログを表示し、ユーザーが「キャンセル」を選択した場合は遷移を中止する。
  • イベントトラッキング: Google Analyticsなどの分析ツールにリンククリックイベントを送信する。
  • 簡単な状態更新: リンククリックに関連するアプリケーションの状態を更新する。

onClickハンドラ内で event.preventDefault() を呼び出すことで、デフォルトのLinkによるナビゲーション動作をキャンセルできます。これにより、独自のロジックに基づいて遷移を制御することが可能になります。

“`jsx
function DeleteButtonWithConfirmation({ itemId }) {
const navigate = useNavigate(); // v6のプログラム遷移用フック

const handleClick = (event) => {
// デフォルトのLinkの遷移を防止
event.preventDefault();

// 確認ダイアログを表示
if (window.confirm('本当に削除しますか?')) {
  // ユーザーがOKを選択した場合のみ処理を実行
  console.log(`アイテム ${itemId} を削除します...`);
  // 削除API呼び出しなど...

  // 処理完了後、別のページへ遷移
  // ここで navigate を使うのが一般的です
  navigate('/items');
}
// ユーザーがキャンセルを選択した場合は何もせず、Linkによる遷移も発生しない

};

// 注意: この例では、Linkは削除処理のトリガーとしてのみ使われ、
// to に指定されたパスへの遷移は preventDefault でキャンセルされています。
// 削除ボタンは通常 ;
}

// 例: フォーム送信後の遷移 (useNavigate)
import { useNavigate } from ‘react-router-dom’;

function MyForm() {
const navigate = useNavigate();

const handleSubmit = (event) => {
event.preventDefault();
// フォームデータの処理、API送信など…
// 処理成功後
navigate(‘/success’, { state: { message: ‘フォームが送信されました’ } });
};

return (

{/ … input要素など /}

);
}
“`

ユーザーがクリックしてナビゲートするためのUI要素として最も直接的に対応するのがLink(またはNavLink)です。プログラムからトリガーされる遷移にはuseNavigateを使います。

実用的な例とサンプルコード

これまで学んだLinkコンポーネントの使い方を、具体的なシナリオで見ていきましょう。

例1: ヘッダーナビゲーション

アプリケーションのヘッダーに、主要なページへのリンクを配置する一般的な例です。アクティブなリンクを強調するためにNavLinkを使用することが多いですが、ここではLinkを使った基本的な例を示します。

“`jsx
// components/Header.js
import React from ‘react’;
import { Link } from ‘react-router-dom’;
import ‘./Header.css’; // スタイリング用のCSSファイル

function Header() {
return (

);
}

export default Header;

// App.js (抜粋)
import { Routes, Route } from ‘react-router-dom’;
import Header from ‘./components/Header’;
// 他のページコンポーネントをインポート…

function App() {
return (




} />
} />
} />
{/ …他のルート /}

);
}
``
この例では、
Headerコンポーネント内のLinkをクリックすることで、対応するパスに遷移し、

内の`で適切なページコンポーネントがレンダリングされます。

例2: リスト項目からの詳細ページへのリンク

ユーザー一覧や商品一覧のようなリストから、個別の詳細ページへ遷移する際に、動的なパスを持つLinkがよく使われます。

“`jsx
// components/UserList.js
import React from ‘react’;
import { Link } from ‘react-router-dom’;

function UserList({ users }) {
return (

    {users.map(user => (

  • {/ to={/users/:userId} の形に動的に生成 /}
    /users/${user.id}}>
    {user.name}
  • ))}

);
}

export default UserList;

// pages/UsersPage.js (抜粋)
import React, { useState, useEffect } from ‘react’;
import UserList from ‘../components/UserList’;

function UsersPage() {
const [users, setUsers] = useState([]);

useEffect(() => {
// 仮のユーザーデータ取得
const fetchUsers = async () => {
// 実際のアプリケーションではAPIからデータを取得
const data = [
{ id: 1, name: ‘Alice’ },
{ id: 2, name: ‘Bob’ },
{ id: 3, name: ‘Charlie’ }
];
setUsers(data);
};
fetchUsers();
}, []);

return (

Users

);
}

// App.js (ルーティング設定抜粋)
import { Routes, Route } from ‘react-router-dom’;
import UsersPage from ‘./pages/UsersPage’;
import UserDetailPage from ‘./pages/UserDetailPage’;

function App() {
return (

} />
{/ パスパラメータ :userId を定義 /}
} />
{/ …他のルート /}

);
}

// pages/UserDetailPage.js
import React from ‘react’;
import { useParams } from ‘react-router-dom’;

function UserDetailPage() {
const { userId } = useParams(); // URLからパスパラメータを取得

// userId を使ってユーザー詳細データを取得・表示
return (

User Detail

User ID: {userId}

{/ …ユーザー情報の表示 /}

);
}
``UserListコンポーネントでは、各ユーザーオブジェクトのidを使って、詳細ページへのパス/users/${user.id}を生成しています。UserDetailPageコンポーネントでは、useParamsフックを使ってURLからuserId`を取得し、そのIDに基づいたデータを表示します。

例3: 検索結果からの遷移 (クエリストリングの使用)

検索フォームから検索結果ページへ遷移し、検索クエリをクエリストリングとして渡す例です。検索結果リストの項目から、そのアイテムの詳細ページへ遷移するリンクも含むことがあります。

“`jsx
// components/SearchBar.js
import React, { useState } from ‘react’;
import { useNavigate } from ‘react-router-dom’;

function SearchBar() {
const [query, setQuery] = useState(”);
const navigate = useNavigate(); // フォーム送信は navigate が適している

const handleSearch = (event) => {
event.preventDefault();
if (query.trim()) {
// /search?q=ユーザー入力値 へ遷移
navigate(/search?q=${encodeURIComponent(query.trim())});
}
};

return (

setQuery(e.target.value)}
placeholder=”検索キーワード”
/>

);
}

export default SearchBar;

// pages/SearchResultPage.js
import React from ‘react’;
import { useLocation, Link } from ‘react-router-dom’; // Link も使う可能性あり

function SearchResultPage() {
const location = useLocation();
const params = new URLSearchParams(location.search);
const searchQuery = params.get(‘q’); // クエリストリングから ‘q’ の値を取得

// 仮の検索結果
const results = searchQuery ? [{ id: 101, title: Result for ${searchQuery} }, { id: 102, title: Another result for ${searchQuery} }] : [];

return (

検索結果

{searchQuery ? (
<>

検索キーワード: “{searchQuery}”

    {results.map(item => (

  • {/ 検索結果アイテムの詳細ページへのリンク /}
    {/ 例: /items/101 /}
    /items/${item.id}}>{item.title}
  • ))}

) : (

検索キーワードを入力してください。

)}

);
}

// App.js (ルーティング設定抜粋)
import { Routes, Route } from ‘react-router-dom’;
import SearchBar from ‘./components/SearchBar’;
import SearchResultPage from ‘./pages/SearchResultPage’;
import ItemDetailPage from ‘./pages/ItemDetailPage’; // 仮の詳細ページコンポーネント

function App() {
return (



} />
} />
{/ …他のルート /}

);
}
// pages/ItemDetailPage.js (例)
import React from ‘react’;
import { useParams } from ‘react-router-dom’;

function ItemDetailPage() {
const { itemId } = useParams();
// itemId を使ってアイテム詳細データを取得・表示
return (

Item Detail: {itemId}

{//}

);
}

``
この例では、検索フォームの送信には
useNavigateを使っていますが、検索結果一覧から個別アイテムへの遷移にはLinkを使用しています。SearchResultPageではuseLocation`を使ってクエリストリングを取得し、表示内容を変えています。

例4: 遷移先に状態を渡す (state)

特定のページから別のページへ遷移する際に、URLに表示させたくないが、遷移先のコンポーネントで利用したい一時的なデータを渡す場合にstatePropsが役立ちます。

“`jsx
// pages/ProductsPage.js (商品一覧)
import React from ‘react’;
import { Link } from ‘react-router-dom’;

function ProductsPage() {
const products = [
{ id: 1, name: ‘Laptop’, price: 1200 },
{ id: 2, name: ‘Keyboard’, price: 75 },
{ id: 3, name: ‘Mouse’, price: 25 }
];

return (

Products

    {products.map(product => (

  • {/ 商品詳細ページへのリンクに、価格情報を state として渡す /}
    /products/${product.id}}
    state={{ referrer: ‘products-list’, initialPrice: product.price }}
    >
    {product.name} – ${product.price}
  • ))}

);
}

// pages/ProductDetailPage.js (商品詳細)
import React from ‘react’;
import { useParams, useLocation } from ‘react-router-dom’;

function ProductDetailPage() {
const { productId } = useParams();
const location = useLocation();
const state = location.state; // state を取得

console.log(‘Location state:’, state); // { referrer: ‘products-list’, initialPrice: 1200 } などが表示される

// productId を使って商品詳細データを取得・表示
return (

Product Detail: {productId}

{state && state.initialPrice && (

Initial Price (from list): ${state.initialPrice}

)}
{/ …詳細情報の表示 /}

);
}

// App.js (ルーティング設定抜粋)
import { Routes, Route } from ‘react-router-dom’;
import ProductsPage from ‘./pages/ProductsPage’;
import ProductDetailPage from ‘./pages/ProductDetailPage’;

function App() {
return (

} />
} />
{/ …他のルート /}

);
}
``
この例では、商品一覧ページから商品詳細ページへ遷移する際に、商品リストから取得した一部の情報(
referrerinitialPrice)をstateとして渡しています。商品詳細ページでは、useLocation().stateを使ってその情報を受け取り、表示や処理に利用できます。これは、API呼び出しを減らしたり、遷移元のコンテキスト情報を提供したりするのに便利です。ただし、state`はリロードで失われるため、永続化が必要なデータや、直接URLからアクセスした場合でも表示できる必要があるデータには適していません。

トラブルシューティングとよくある落とし穴

Linkコンポーネントを使う上で遭遇しやすい問題と、その解決策をいくつか紹介します。

  1. Linkが機能しない、フルリロードが発生する:

    • 原因1: アプリケーション全体がBrowserRouter(または他のRouterコンポーネント)で囲まれていない。
      • 解決策: アプリケーションのルートコンポーネント(例: App)や、ルーティングを使用する部分全体をreact-router-domからインポートしたBrowserRouterなどのRouterコンポーネントで囲んでください。
    • 原因2: Linkではなく<a>タグを使っている。
      • 解決策: SPA内部のナビゲーションには<a>タグではなくLinkコンポーネントを使用しているか確認してください。
    • 原因3: target="_blank" などを指定している。
      • 解決策: target="_blank"は新しいタブ/ウィンドウで開く指示であり、多くの場合フルリロードを伴います。SPA内部リンクで新しいタブを開くのはユーザー体験上一般的ではないため、基本的にはtarget属性は使用しません。もし外部サイトへのリンクで新しいタブを開きたいなら、迷わず<a>タグを使いましょう。
    • 原因4: onClickハンドラ内でevent.preventDefault()を呼び出しているが、その後useNavigateなどでプログラムによる遷移を行っていない。
      • 解決策: onClickでデフォルトの遷移を阻止した場合、自分でプログラム的に遷移をトリガーするか、あるいはpreventDefaultを条件付きで呼び出す(例: 確認ダイアログでキャンセルされた場合のみ)ようにロジックを見直してください。
  2. 相対パスが意図しないパスに解決される:

    • 原因: 現在のURLパスに対する相対的な解決ルール(relative="path")と、期待する遷移先パスが一致しない。特にネストされたルーティングで発生しやすい。
    • 解決策:
      • toPropsに絶対パスを使用することを検討する。
      • 現在のURLとtoに指定した相対パスがどのように解決されるか、紙やコンソールで確認してみる。
      • React Router v6.4以降を使っている場合は、relative="route"Propsが役立つか検討する。しかし、多くの場合デフォルトのrelative="path"が直感的です。
      • どうしても相対パスでの解決が難しい場合は、useNavigateuseResolvedPathフックを組み合わせて、プログラムで相対パスを解決し、その結果を使って絶対パスで遷移するという方法もあります(上級)。
  3. stateが遷移先で取得できない:

    • 原因1: statePropsまたはtoオブジェクトのstateプロパティで正しくstateを渡せていない。
      • 解決策: LinkコンポーポーネントのPropsでstate={{ key: value }}またはto={{ pathname: '...', state: { key: value } }}のように指定しているか確認してください。
    • 原因2: 遷移先のコンポーネントでuseLocation().stateを正しく取得できていない。
      • 解決策: 遷移先のコンポーネントでimport { useLocation } from 'react-router-dom'; const location = useLocation(); const state = location.state; のように正しく取得しているか確認してください。
    • 原因3: 遷移先のページが直接URLでアクセスされた(例: ブラウザのリロード、ブックマーク、URLの直接入力)。
      • 解決策: stateはブラウザの履歴エントリに紐づく一時的な情報です。リロードや直接アクセスでは失われます。stateに依存する表示や処理を行う場合は、stateが存在しない場合のフォールバックロジック(例: データを再取得する、エラーメッセージを表示するなど)を実装する必要があります。永続化が必要なデータは、クエリストリング、パスパラメータ、ローカルストレージ、またはサーバー側で管理することを検討してください。
  4. スタイリングが効かない、または意図しないスタイルが適用される:

    • 原因1: CSSセレクタが正しくない。Linkコンポーネントは最終的に<a>タグとしてレンダリングされるため、CSSではa要素や指定したclassNameに対してスタイルを適用します。
    • 解決策: 開発者ツールでレンダリングされたHTMLを確認し、要素構造やクラス名を確認してください。CSSセレクタが正しく要素をターゲットにしているか確認してください。
    • 原因2: NavLinkと間違えている、またはNavLinkのアクティブスタイルが期待通りに適用されない。
      • 解決策: アクティブスタイリングが必要な場合はNavLinkを使用しているか確認してください。NavLinkのアクティブクラス名やスタイルはカスタマイズ可能です (activeClassName, activeStyle Props – v5、v6では関数形式のclassName/style Propsを使用)。ルーティング設定(特にネストされたルートやインデックスルート)がNavLinkのパスと正確にマッチしているか確認してください。

これらのよくある問題を理解しておくことで、開発中のデバッグ時間を減らすことができます。

まとめ

この記事では、React RouterにおけるSPAナビゲーションの要であるLinkコンポーネントについて、その基礎から応用、そして他のナビゲーション手段との比較まで、詳細に解説しました。

Linkコンポーネントは、HTMLの<a>タグのように見えますが、クリックされた際にブラウザのフルリロードを防ぎ、History APIを利用してReact Routerによるクライアントサイドルーティングをトリガーするという重要な役割を担っています。これにより、SPAのスムーズで高速なページ遷移を実現します。

Linkコンポーネントの主要なポイントをまとめます。

  • SPA内部のルート間を遷移するための主要なUIコンポーネントです。
  • 必須のtoPropsで遷移先のパスを指定します。文字列またはオブジェクトで指定でき、オブジェクト形式ではpathname, search, hash, stateなどを細かく設定できます。
  • replace={true}Propsで、履歴スタックに新しいエントリを追加する代わりに現在のエントリを置き換えることができます。
  • statePropsまたはtoオブジェクトのstateプロパティで、遷移先のコンポーネントに一時的なデータを渡すことができます(リロードで失われます)。
  • 相対パス (/ で始まらない) は、デフォルトでは現在のURLを基準に解決されます(relative="path")。v6.4+ではrelative="route"でルート定義パス基準に変更できます。
  • onClickハンドラで、遷移前のロジックを実行したり、event.preventDefault()でデフォルトの遷移をキャンセルしたりできます。
  • HTMLの<a>タグの多くの標準属性(className, style, titleなど)をPropsとして受け付けます。
  • アクティブ状態のスタイリングが必要な場合は、Linkの代わりにNavLinkを使用するのが一般的です。
  • 外部サイトへのリンクやフルリロードが必要な場合は、HTMLの<a>タグを使用すべきです。
  • プログラムによる遷移にはuseNavigateフックを使用します。

Linkコンポーネントは、React Routerを使ったSPA開発において最も頻繁に利用されるコンポーネントの一つです。その使い方と、Propsによるカスタマイズ方法をしっかりと理解することは、ユーザーフレンドリーで効率的なアプリケーションを構築するために不可欠です。

この記事で学んだ知識を活かして、あなたのReactアプリケーションに快適なナビゲーションを実装してみてください。さらに深いルーティングの概念(ネストされたルーティング、認証付きルート、データローディングなど)や、NavLink, useNavigateなどの他のReact Router機能についても学ぶことで、より高度なSPA開発が可能になります。

この解説が、あなたのReact RouterおよびLinkコンポーネントの理解を深める一助となれば幸いです。Happy Coding!


およそ5000語となるように、各セクションを詳細に記述し、例を豊富に含めました。要件通り、記事の内容を直接表示しています。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール