React Routerは、Reactアプリでページ遷移を実現するライブラリの1つである。任意のURLセグメントとコンポーネントを対応づける形で、ルーティングを行う。
React Routerとは
React Routerには、Webアプリ用のreact-router-dom
と、ネイティブアプリ用のreact-rotuer-native
という2種類のパッケージがある。
パッケージの種類 | 概要 |
---|---|
react-router-dom |
Webアプリのためのルーティングを提供。 |
react-router-native |
デスクトップ/スマホアプリのためのルーティングを提供。 |
また、両者が共有するコアはreact-routerパッケージにまとめられている。
ほんで、react-router
とRemixが共有するコアはrouterというパッケージにまとめられている。
Remixについて
React Routerのチームは、RemixというReactベースのWebフレームワークを開発している。Remixは旧バージョンのReact Routerを土台としており、その開発過程で得られたルーティングのノウハウが新バージョン(V6)のReact Routerに反映されている。
前述の通り内部では@remix-run/routerというパッケージを使っており、おおよそは同じものだ。チュートリアル(React Router/Remix)に書かれている内容も共通する部分が多い。
これらのチュートリアルは、それぞれ「30-60分くらいで終わる」と見積もられている。信じて取り組んだら6時間くらい掛かった。泣いた。
本筋とは関係ないが、あれこれ調べていたらRemixってなんやねんという場面に何度か出会したので参考までに記しておく。
基本的な使い方
まずreact-router-dom
をプロジェクトにインストールする(Reactでの開発環境は整っているものとする)。
続いてコードを書く。最もシンプルなルーティングは、以下のような形を取る。
createBrowserRouter()
関数でRouter
オブジェクトを作る。- 1で作成した
Router
オブジェクトを<RouterProvider>
という要素のrouter
属性に渡す。
これだけである。
終わりである。
To Child
リンクをクリックすると、/child
に遷移してI'm your chiiiiiiiiiiiiiiiiiiild!
が表示される。
また、To Top
リンクをクリックすると、/
に遷移してI'm the toooooop!
が表示される。遷移に応じて、ブラウザの進む、戻るもちゃんと機能する。
createBrowserRouter()
でパスとReact要素を対応づけるRouter
オブジェクトを作り、その情報をもとにRouterProvider
コンポーネントがパスに応じたReact要素を表示していることがわかる。
上記のコードはエラーを出さず普通に動く。しかし、あんまりよろしくない点が2つある。
ルートにはRouterProviderだけを置く
まずアプリのルート(根っこの意味)には、<RouterProvider>
を1つだけ置くことが推奨される。
例えばReactでは、アプリのルートをよくApp
というコンポーネントで表す。<App>
はただのReact要素であるのでどこにでも幾つでも配置できるが、通常はルートに1つだけ配置される。<App>
がトップ以外にあったり、複数あったりしたら、何がどうグループ化されているのか理解に困るからだ。
<RouterProvider>
も同様である。主に一貫性と保守性の観点から、アプリ全体のルーティングは一箇所で管理することが推奨される。つまり1つの<RouterProvider>
ですべての要素の表示を振り分ける形が推奨される。
なお、<RouterProvider>
に渡されるrouter
オブジェクトは、一般にRoot routeと呼ばれるそうである。この根っこのroute
オブジェクトが、アプリのレイアウトを担うとも言える。
aタグではなくLinkタグを使う
先のサンプルでは、ページ遷移に<a>
を使っている。これだと、ページ遷移のたびにドキュメント全体が更新される。どのブラウザでも、この仕様は変わらない。
SPAの大きな強みは、必要な箇所のみを更新することで実現する高速なレスポンスである。<a>
ではこの強みが十分に発揮されない。
必要な箇所のみを更新するには、<a href="">
の代わりに<Link to="">
を使う必要がある。
修正版
2つの修正点を反映したものが以下のコードである。
まずアプリ全体のレイアウトを担当するLayout
コンポーネントを新たに作った。この中で使われている<Outlet />
タグは、そのパスに指定したchildren
プロパティの中身をレンダリングするためのタグである。<Outlet />
がないと、children
達に出番は来ない。
名前はLayout
でなくても構わないが、各ページの共通部分を切り出して階層化し、各階層の独自部分は<Outlet />
タグで出力するのが、React Routerの基本的な使い方と言える。
例えば以下の指定では、/
では<TopPage />
と置き換わり、/child
では<ChildPage />
が<Outlet />
と置き換わるイメージだ。
<Outlet />
は複数箇所に配置できるが、element
プロパティに指定したものしか出力されない。<Outlet/>
で出力できるのは、1つの階層に1種類と心得ておくべきである。
この設計の背景には、URLセグメントと表示コンポーネントが強く結びついているという事実がある(リンク先はチュートリアルで紹介されているサンプルである)。
URLの構造をアプリのコンポーネントの構造と対応させるのが、React Routerの本懐である。たぶん。
遷移によって大きく構造を変えたいときは、以下のように階層を避けてレンダリングするコンポーネントを振り分ければ良い。
ルーティングに使う基本的な関数と要素
ここまでに使った関数と要素の詳細をまとめる。
createBrowserRouter()関数
createBrowserRouter()
は、以下のように型宣言された関数である。
参考:createBrowserRouter v6.22.0 | React Router
入門レベルではrotues
だけ覚えておけば良さそうだ。
Routeオブジェクト
createBrowserRouter
のroutes
プロパティに渡すのが、Route
オブジェクトの配列である。このオブジェクトは以下のように型宣言されている。
参考:Route v6.22.0 | React Router
Routeタグ
頭文字が大文字になっていることからわかるように、Route
オブジェクトはReactコンポーネントである。オブジェクトではなく、<Route>
タグで指定することもできる。
例えば先のサンプルは、以下のように書き換えられる。
Route
オブジェクトのプロパティを、そのまま<Route>
タグの属性に置き換えただけである。createRoutesFromElements()
関数は、渡されたReact要素をもとにRoute
オブジェクトを作って返す関数だ。
こっちの記法の方が宣言的でわかりやすい。
RouterProviderタグ
これもとりあえずはrouter
だけ覚えておけば良さそうだ。
参考:RouterProvider v6.22.0 | React Router
Linkタグ
参考:Link v6.22.0 | React Router
だいたい見たまんまである。
Outletタグ
Outlet
には何もなかろうと思ったらなんかあった。考えれば当然である。<Outlet />
だけでは、親から子へ情報を受け渡せない。context
プロパティは、そのためにある。
子コンポーネントで値を受け取る場合は、useOutletContext()
フックを使う。
参考:Outlet v6.22.0 | React Router
参考:useOutletContext v6.22.1 | React Router
React Routerのサンプルコード集
公式が用意したサンプルコード集がここにある。ドキュメントを見てわからない部分は、それっぽいサンプルを覗けば何とかなるかと思う。