目次
概要
この記事では、ReactによるWebアプリケーションでReact Routerというライブラリを用いてルーティングを行う方法について説明しています。この記事を読んで、React Routerについて少しでも理解を深めていただけるようであれば幸いです。
前提条件
- アプリケーションの作成にはCreate React Appを用いている
- Reactに関してある程度の知識がある(Reactの基礎についてはこちらの記事にもまとめていますので併せて参考にしてみてください)
環境
| 導入ライブラリ | version |
|---|---|
| create-react-app | 3.4.1 |
| react | 16.3.1 |
| react-dom | 16.3.1 |
| react-router-dom | 5.2.0 |
React Routerとは
React Routerとは、Reactを用いて作成したSPA(Single Page Application)に対して、UIとURLを対応づけるためのライブラリです。
例えばTodoアプリの場合、http://sample.com/loginにアクセスした時にはログインページを表示するLoginコンポーネントをレンダリングし、http://sample.com/todoにアクセスした時にはTodo一覧を表示するTodoListコンポーネントをレンダリングするといった操作が可能になります。

ライブラリ
React Routerを使用するためには、作成しているアプリケーションにライブラリのインストールが必要です。React Routerに関係するライブラリにはいくつか種類があるので、用途に合わせてどのライブラリが必要なのか抑えておく必要があります。
| ライブラリ | 説明 |
|---|---|
| react-router | ナビゲーションするためのコアライブラリ |
| react-router-dom | DOMベースアプリのナビゲーションを行うためのライブラリ |
| react-router-native | ネイティブアプリのナビゲーションを行うためのライブラリ |
| react-router-redux | ReduxとReact Routerをバインディングするためのライブラリ(<Link>などで事足りる場合やhistoryに触れる状況を作ってある場合は不要) |
今回の場合はDOMベースのWebアプリケーションでのルーティングを行うため、react-router-domを使用します。
react-router-domはコアライブラリであるreact-routerの上位互換のようなものなので、基本的にはreact-router-domを使用すれば大丈夫でしょう。
React Routerの仕組み
はじめにコードの書き方を見てみましょう
import React from 'react';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import Top from './components/Top';
import Login from './components/Login';
import TodoList from './components/TodoList';
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Top} />
<Route path='/login' component={Login} />
<Route path='/todo' component={TodoList} />
</Switch>
<Link to='/'>Back To Top</Link>
</BrowserRouter>
);
};
このように記述することで上図のようにpathによるルーティングを可能にできます。
ここでhistoryとはオブジェクトで、JavaScriptが実行される場所であれば履歴の保存やナビゲートの管理などを行ってくれています。react-router-domのBrowserRouterを使用した場合にはこのhistoryオブジェクトを自動的に作成してくれます。
Routerの種類
上記で示したBrowserRouterはRouterの上位コンポーネントでその他にもいくつか種類がありますのでここで紹介します。
| 種類 | 説明 | URL例 |
|---|---|---|
| BrowserRouter | TLD(.jpとか.comとか)やportのあとの部分をすべてpathとして使用する | localhost:3000/todo |
| HashRouter | #以降の全てをpathとして使用する | localhost:3000/#/todo |
| MemoryRouter | ナビゲートにURLを使用しない | localhost:3000 |
他にも種類がありますので用途に応じて使い分けるようにするといいと思います。
exactの役割
<Route />にexactを記述することで、<Route />内に記述されたpathがlocation.pathnameに完全に一致した場合にのみ該当するコンポーネントを返すようになります。
exactはデフォルトではfalseですが、<Route />に記述することでtrueとなります。
公式ドキュメントでは以下のように記述されています。
| path | location.pathname | exact | matches? |
|---|---|---|---|
| /one | /one/two | true | no |
| /one | /one/two | false | yes |
exactがfalseの場合、指定したpathが/oneでもlocation.pathnameの/one/twoに部分一致してしまうため表示されることになります。完全一致した場合のみ表示させたい時には、exactを記述しましょう。
上記のコードを少し書き換えて以下のようにした場合を考えてみます。
// 省略
return (
// 省略
// exactを使用しない場合
<Route path='/' component={Top} />
<Route path='/login' component={Login} />
<Route path='/todo' component={TodoList} />
// 省略
);
ローカル環境でアプリケーションを表示しているときにhttp://localhost:3000/todoにアクセスした場合、何が表示されるでしょうか。
この場合、pathは/todoなのでTodoListコンポーネントが表示されるのは明らかです。またpathには/も部分一致しているので、Topコンポーネントも同時に表示されます。
次にexactを使用した場合を考えます。
// 省略
return (
// 省略
// exactを使用した場合
<Route exact path='/' component={Top} />
<Route path='/login' component={Login} />
<Route path='/todo' component={TodoList} />
// 省略
);
**この場合、path='/'にexactを使用しているのでTopコンポーネントはpathが'/'に完全一致した場合のみ表示されることになります。**そのためhttp://localhost:3000/todoにアクセスした場合には、TodoListコンポーネントのみが表示されます。
Linkコンポーネント
<Link>コンポーネントはHTMLでいう<a>タグとほぼ同じです。
ただし<Link>はプロパティとして関数を記述できたりとか色々できることが<a>タグと比較して多いようです。
詳しくは公式ドキュメントを見ていただければいいと思います。
基本的な使い方を見てみましょう。
<Link to='/'>Back To Top</Link>
この<Link>の中でtoにpathを指定することで、ここのリンクをクリックしたときにこのpathが含まれている<Route>のコンポーネントを表示することができます。
今回の場合は<Route path='/' component={Top} />となっているのでTopコンポーネントが表示されることになります。
Switchコンポーネント
続いて<Switch>コンポーネントについて紹介します。
はじめに以下の場合どのようになるか考えてみます。
path='/todo/new'でTodoの新規作成画面を表示、path='/todo/:id'でidに応じたTodoの個別の詳細画面を表示したいとき、今まで紹介した内容で記述すると以下のようになると思います。
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import NewTodo from './components/NewTodo';
import ShowTodo from './components/ShowTodo';
const App = () => {
return (
<BrowserRouter>
<Route path='/todo/new' component={NewTodo} />
<Route path='/todo/:id' component={ShowTodo} />
</BrowserRouter>
);
};
このときpath='/todo/:id'という記述方法が見慣れないかもしれませんが、このように記述することで**:idの部分には何でも入れることができます。**今回はTodoのidに応じて個別の画面を表示したいので、期待されるpathはpath='/todo/1'とかpath='/todo/2'のようなものです。
しかしここには問題点があります。
上述したように**「:idには何でも入る」ということに注目しましょう。今回の場合では、新規作成画面NewTodoコンポーネントとTodo個別詳細画面ShowTodoコンポーネントのpathがそれぞれ/todo/...となっています。
つまり:idの部分にはnewも入ってしまう**ということです。したがって、Todoの新規作成画面を表示したときにはNewTodoコンポーネントとShowTodoコンポーネントの両方が表示されることになります。
このようなときに使用するのが<Switch>です。
では書き方を見てみましょう。
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import NewTodo from './components/NewTodo';
import ShowTodo from './components/ShowTodo';
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route path='/todo/new' component={NewTodo} />
<Route path='/todo/:id' component={ShowTodo} />
</Switch>
</BrowserRouter>
);
};
このように記述することで、<Switch>...</Switch>の中に入っている<Route>コンポーネントから1つだけを表示できるようになります。
ただしここで注意点があります。
<Switch>...</Switch>の内部では上から順に見ていくので**<Route>の配置順が重要**になります。上記のコードの場合、pathに/todo/newが指定されたときにはNewTodoコンポーネントが表示されます。
しかし下記のように配置順を逆にした場合、
<Switch>
<Route path='/todo/:id' component={ShowTodo} />
<Route path='/todo/new' component={NewTodo} />
</Switch>
:idには何でも入るのでpathに/todo/newを指定したときには先に上の<Route>が反応してしまうので、ShowTodoコンポーネントが表示されることになります。
もし<Switch>を使用しているときに、レンダリングがうまくいかないときには<Route>の配置順を見直してみるといいかもしれません。
Intentional NavigationとProgrammatic Navigation
Intentional NavigationとProgrammatic Navigationについて簡単に説明します。
-
Intentional Navigation:ユーザが
<Link>をクリックしたとき移動 - Programmatic Navigation:アプリを介してユーザを強制的に移動するコードを実行
Intentional Navigationについてはここまでに説明した内容で実装できると思いますが、Programmatic Navigationについてはもう少し詳しく見ていく必要があります。
関数などで何か処理をさせた後にページを移動させる事例としては、Reduxを使用している場合に多いと思います。何かのボタンを押したときにActionを実行し、ReducerでStore内のState操作を行った後にページを移動させるなどの処理が考えられます。このような場合にはProgrammatic Navigationにする必要があります。
ここで関係してくるのがhistoryオブジェクトです。ページ遷移の仕方には<Link to='/path'>をクリックする場合と、history.push('/path')のように関数として実行することができます。
これまでの内容では<BrowserRouter>を使用して自動的にhistoryを作成していましたが、history.push()などを扱うためにはhistoryを自身で用意する必要があります。
では実際の使い方を見てみましょう。
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
historyオブジェクトの作成はこれだけです。あとはhistory.push()などを扱いたいファイルでインポートすることで使用できます。
次に<Route>などを記述しているApp.jsについて見てみます。
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import Top from './components/Top';
import Login from './components/Login';
import TodoList from './components/TodoList';
import history from `history`; // これ重要
const App = () => {
return (
<Router history={history}>
<Switch>
<Route exact path='/' component={Top} />
<Route path='/login' component={Login} />
<Route path='/todo' component={TodoList} />
</Switch>
</Router>
);
};
ここではhistoryオブジェクトを自身で作成しているので、<BrowserRouter>ではなく<Router>を使用します。そして<Router history={history}>と記述することで内部で示しているpathとhistory.push()などで指定するpathを関連付けてあげることができます。
ここで気がつくかもしれませんが**<BrowserRouter>はRouterのwrapper**だったということがわかりますね。BrowserRouterでは内部で同じことをやっています。(BrowserRouterのソースコードはこちら)
あとは処理としてページ遷移を行わせたいところで、
import history from 'history';
// ...
// いろいろな処理
history.push('/todo')
みたいな感じで使用すれば記述してあげればOKです。historyのインポートだけ忘れないようにしましょう。
まとめ
少し長めの記事になってしまいましたが、今回はReact Routerの基本的な使い方について説明しました。
簡単にまとめたいと思います。
-
React Routerとは
- Reactを用いて作成したSPAに対して、UIとURLを対応づけるためのライブラリ
-
React Routerの仕組み
- Routerにもいくつか種類がある
-
pathやhistoryオブジェクトを巧みに使用する -
exactや<Switch>などの使い方次第でルーティング処理が変わるので注意が必要
-
Intentional NavigationとProgrammatic Navigation
- 処理としてページ遷移を組み込む場合には
history.push()
- 処理としてページ遷移を組み込む場合には
今回紹介した内容以外にも、React Routerには様々なAPIがあるので用途に合わせた使い方をしてみてください。

