React の Router 系コンポーネント (BrowserRouter
/ HashRouter
/ MemoryRouter
) を使用すると、論理的なパス管理によって React コンポーネントの表示の On/Off を切り替えることができます。
例えば、/users
というパスでアクセスしたときには Users
コンポーネントを表示し、/projects
というパスでアクセスしたときには Projects
コンポーネントを表示する、といったことを実現できます。
react-router-dom のインストール
Router 系のコンポーネントは react-router-dom
モジュールで提供されているので、まずはこれをインストールします。
TypeScript を使用する場合は、型定義ファイルも一緒にインストールしておきます。
BrowserRouter による表示切り替え
次の App
コンポーネントでは、BrowserRouter コンポーネントを使って、Page1
と Page2
コンポーネントの表示を切り替えます。
Router 系のコンポーネントには、Router
というエイリアス名を付けるのが慣例なのでそれに従います。
Router
コンポーネントの下には、表示切り替え用のリンク要素として Link コンポーネントを配置し、表示要素として Route コンポーネントを配置します。
あとは、この App
コンポーネントを次のように表示します。
Link
コンポーネントとして表示された Page1 や Page2 リンクをクリックすると、Page1 や Page2 コンポーネントの内容が表示されます。
☝️ Uncaught DOMException: Failed to execute 'pushState' on 'History'
BrowserRouter
を動作させるには、Web サーバー上の HTML ファイルをブラウザで開いている必要があります。
ローカルの HTML ファイルを使って動作させたい場合は、BrowserRouter
の代わりに HashRouter
や MemoryRouter
を使用すると動作します。
component パラメータによる表示コンポーネントの指定
下記のサンプルコードでは、Route
コンポーネント以下にネストする形で子コンポーネント(Page1
や Page2
)を指定しています。
このように子コンポーネント指定が単純な場合は、次のように Route
の component プロパティ を使って表示するコンポーネントを指定してしまうこともできます。
この書き方をすると入れ子が減ってスッキリしますが、細かいパラメータ指定がやりにくかったり、実はタイプ数が増えていたりするので、無理にこの書き方をする必要はないと思います。
パスが完全一致を必須とする (exact)
Route
コンポーネントは、現在のパスと path
プロパティの値が一致した場合に子コンポーネントを表示するものですが、この一致判断は、デフォルトでは前方一致 により行われます。
例えば、次のような Route
コンポーネントが配置されているとします。
このとき、現在のパスが /page1/xxx
だとすると、Home
コンポーネントと Page1
コンポーネントの両方が同時に表示されます。
現在のパス | 表示されるコンポーネント |
---|
/ | Home |
/page1 | Home と Page1 |
/page1/xxx | Home と Page1 |
/page2 | Home と Page2 |
/page2/xxx | Home と Page2 |
パスの一致判断を、前方一致ではなく完全一致にするには、次のように exact
プロパティを指定します。
このように記述しておくと、Home
と Page
コンポーネントに関しては、パスが完全一致したときのみ表示されるようになります。
現在のパス | 表示されるコンポーネント |
---|
/ | Home |
/page1 | Page1 |
/page1/xxx | ─ |
/page2 | Home と Page2 |
/page2/xxx | Page2 |
排他的にコンポーネントを切り替えて表示する (Switch)
これまでの例で気付いたと思いますが、Router
以下に配置した Route
コンポーネントは、排他的に切り替え表示されるものではなく、あくまで各々の Route
コンポーネントが個別に表示の On/Off を判断するものです。
複数の Route
コンポーネントのうち、いずれか 1 つのみを表示したい場合、つまり、ページ切り替えをしたい場合 は、次のように Switch コンポーネント 以下に Route
を配置するようにします。
Switch
以下に Route
を配置すると、上から順番に見て、最初にパスが一致したもののみが表示されるようになります。
ルートパス (/
) には必ず一致してしまうので、path="/"
指定されたものは他の Route
よりも後ろに配置しなければいけない ことに注意してください。
もちろん、exact
指定をしている場合は先頭に配置することが可能です。
上記コードの最後の Route
コンポーネントには path
の指定がありませんが、このように記述すると、それより前に配置した Route
のいずれにもパスが一致しなかった場合に、必ず表示されるようになります。
つまり、パスが不正な場合の Not Found ページ を表示する場合に使用できます。
現在のパス | 表示されるコンポーネント |
---|
/ | Home |
/page1 | Page1 |
/page1/xxx | Page1 |
/page2 | Page2 |
/page2/xxx | Page2 |
/xxx | NotFound |
リダイレクト (Redirect)
Redirect コンポーネントを使用すると、ユーザーが特定のパスにアクセスしたときに、別のパスにリダイレクトすることができます。
例えば、ルートパス (/
) にアクセスしたときに、デフォルトページとして /page1
にリダイレクトする、といったことが可能です。
これは、次のようにルートパス (/
) にアクセスしたときに表示するコンポーネントを、/page1
にアクセスした場合と同じにするのと似ていますが、同一の内容を表示するのであれば、リダイレクトを使うことをおすすめします。
なぜなら、React 内部のパス情報が変更されないと、NavLink
要素の activeClassName
や activeStyle
属性が動作しないなどの問題が出るからです(NavLink
については後述)。
次のように Switch
と組み合わせて使用すると、どのパスにも一致しない場合にルートパス(Home
ページ)に飛ばす、といったことが可能です。
これは、次のようにデフォルトルートを指定した場合と振る舞いは似ていますが、現在のパスがルート (/
) に置き換わるかどうかの違いがあります。
ルート・パラメーターを使用する(ディープリンク)
Route
コンポーネントの path
プロパティには、可変となるパラメーターを指定することができます。
パラメーター部分で表示する要素の ID などを渡すことで、いわゆる ディープリンク を実現することができます。
次の例では、TODO を表示するための ToDoPage
コンポーネントのためのルーティング定義を行なっています。
最初の Route
の path
には /todos/:id
が指定されており、この :id
の部分が可変のパラメーターになります。
Link
コンポーネントなどで現在のパスが /todos/123
に切り替わると、ToDoPage
コンポーネントに 123
という値が渡されます。
下記に具体的な実装例を示します。
関数コンポーネント (ToDoPage
) の中では、useParams フック を使用することで、渡されたパラメーターを取り出すことができます。
パラメータが省略された場合は、id
の値は undefined
になるので、その場合はすべての TODO を表示する、といった分岐処理が可能です。
上記の例では、単純に ALL と表示しています。
アクティブなページのリンクをハイライトする (NavLink)
React Router によるナビゲーション(現在のパスの変更)には、Link
コンポーネントによるリンクを使いますが、この拡張コンポーネントとして、NavLink が用意されています。
NavLink
を使ってナビゲーションを行うと、そのパスがアクティブ(対象の Route が表示されているとき)の CSS スタイルを指定することができます。
上記の表示例では、Page1 リンク (NavLink
) をクリックしたときに、リンクの背景色を水色に設定しています。
アクティブな NavLink
用の CSS スタイルを直接指定するには activeStyle
属性を使い、CSS クラス名で指定する場合は activeClassName
属性を使います。
style
属性や className
属性で指定した CSS スタイルは、アクティブでないリンクとアクティブなリンクの両方に反映されます。
次のサンプルコードでは、activeStyle
を使ってアクティブなリンクの背景色を設定しています。
次のサンプルコードもほぼ同様ですが、スタイルの直指定ではなく、activeClassName
で CSS クラス名を指定しています。
関連記事