目次
概要
この記事では、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があるので用途に合わせた使い方をしてみてください。