React Roter v4 (RRv4) を使用した時の備忘録

react app を使う際にルーティングとして利用するreact-routerv4になっていたので、ざっくりと触ってみた時の備忘録。

react-routerはv3と大きく違うため、正式なドキュメントを参考にしないとv4以前のものと混同してしまっておそらくはまってしうんじゃないかな。

パッケージ

まず、routerは大きく3つのパッケージに分割されている。

package description
react-router core package
react-router-dom react-routerとdomをbinding
react-router-native react-routerとreact-nativeをbinding

今回はウェブアプリケーションを利用するため react-router-dom を使ってみた。

このpackageには BrowserRouterHashRouterLinkNavLink コンポーネントがある。

アプリケーションに動的なURIが含まれる場合は BrowserRouterを利用する。こちらはbrowserのHistory APIをサポートしている。
HashRouter はwindow.location.hashを使ってルーティングと同期している。
MemoryRouter (URLは変更しないで履歴をメモリ管理するルーター) などcore packageでザポートされているrouterも利用は可能っぽい。

routerコンポーネント作成

BrowserRouter を使う場合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { BrowserRouter, Route } from 'react-router-dom'
const App = () => (
<div className="layout">
<Header />
<Main />
<Footer />
</div>
)
ReactDOM.render((
<BrowserRouter>
<App />
</ BrowserRouter>
), document.getElementById( 'root'))

route と path

v4ではrouteで指定したpathとlocaltion.pathnameが一致したcomponetをレンダリングする。

例えば以下ような場合に

1
2
3
4
5
<Switch>
<Route  path='/' component={HomeComponent}/>
<Route path='/hoge' component={HogeComponent}/>
<Route path='/fuga' component={FugaComponent}/>
</Switch>

/hoge へのアクセスで

  • / がマッチするため HomeComponent をレンダリング
  • /hoge がマッチするため HogeComponent をレンダリング
  • マッチしないため FugaComponent はレンダリングされない

となる。

pathnameなのでurlのパラメーターなどは関係しない。
パスのマッチングには path-to-regexp パッケージが使われている。

### routeコンポーネント作成

実際のルーティング部分を作成する。先ほどの例では <App /> コンポーネントの中のいずれかのコンポーネント内で実際のルーティング処理を記述することになる。

1
2
3
4
5
<Switch>
<Route exact path='/' component={HomeComponent}/>
<Route path='/add' component={AddComponent}/>
<Route path='/edit/:id' component={EditComponent}/>
</Switch>

exact プロパティは location.pathname と値が正確に一致した場合のみの適用される。

routeのレンダリング

Routeコンポーネントのレンダリングには3つの方法がある。

component

指定されたコンポーネントをReact.createElementで作成するため、新しいコンポーネントを作成します。

1
2
3
4
5
<Route path="/user/:username" component={User}/>
const User = ({ match }) => {
return <h1>Hello {match.params.username}!</h1>
}

render

comonentをマウントしないで、インラインでレンダリングすることができる。
ただし component が優先されるらしいので同じ Route で両者を使用するのは避けたほうがいい。

レンダリングするコンポーネントへpropsを渡す時などはこちらを使うとやりやすい感じ。

1
2
3
4
5
6
7
8
9
10
11
// convenient inline rendering
<Route path="/home" render={() => <div>Home</div>}/>
// wrapping/composing
const RouteWrapper = ({ component: Component, ...rest }) => (
<Route {...rest} render={ props => (
<SomeCompoment>
<Component {...props}/>
</SomeCompoment>
)}/>
)

children

pathが条件に一致するかどうかをレンダリングしたい場合などは、children propを使う。
pathと一致するしないに関わらず常にレンダリングされる。

1
2
3
4
5
6
7
8
9
10
11
<ul>
<ItemLink to="/somewhere"/>
</ul>
const ItemLink = ({ to, ...rest }) => (
<Route path={to} children={({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest}/>
</li>
)}/>
)

params

動的なURIの場合、パラメーターはコンポーネントで取得する必要がある。

1
2
3
4
5
<Switch>
<Route exact path='/' component={HomeComponent}/>
<Route path='/add' component={AddComponent}/>
<Route path='/edit/:id' component={EditComponent}/>
</Switch>

上記のような場合、/edit/:idid をコンポーネントで受け取る場合。

1
2
3
4
5
6
7
const EditComponent = props => {
const id = parseInt(props.match.params.id, 10)
return (
<div>id: {id}</div>
)
}

props.match.params で取得できる。

最後にページ遷移を行う場合にLink コンポーネントを利用する。
これによりURLが更新されて、ページをリロードすることなくコンポーネントがレンダリングすることができる。

1
2
3
4
5
6
7
8
9
import { Link } from 'react-router-dom'
<Link to="/hoge">Hoge</Link>
<Link to={{
pathname: '/search',
search: '?q=string',
hash: '#hash-query',
state: { examplestate: true }
}}/>

URLの変更を検知して、popstateを発火しているので、ページ移動のたびにコンポーネントを再描画できるっぽい。
(history.listenを利用)

また、ナビゲーションなど該当ページのURLとリンク先URLがマッチした時にスタイルを適用するとかの処理が必要な場合は、NavLink コンポーネントを使うといい感じ。

1
2
3
4
5
6
import { NavLink } from 'react-router-dom'
<NavLink to="/hoge">Hoge</NavLink>
<NavLink
to="/fuga"
activeClassName="current-page"
>Fuga</NavLink>

まとめ

v3からワイルドに変更されていることで有名なんですが、
Roterが小さなコンポーネントで分割構成されていることで、Reactを使っていると、ルーティング周りは実装しやすい。

ルーター経由でコンポーネントにpropsを渡す時にrenderすることが最適解なのか未だにわかっていない感がある。

作成したサンプル

react-redux-router-ssr-example

参考にしたページ

React Router: Declarative Routing for React.js
ReactTraining/react-router

Comments