Edited at

react-router v4 変更点 Nested Routing など

More than 1 year has passed since last update.

react-router v3 から v4 に Migration した際に得た知見を記録します。


react-router 4.0.0

メジャーバージョンアップのたびに API がワイルドに変更されることで知られている react-router ですが、v4.0 は過去最大級にワイルドな仕上がりになっています。

要点は上記に詳しいです。おもに Routing 周りと location について記載します。


Nested Routing

Nested Routing という呼称で良いか分からないですが、v3 までは Single Page Application を実装する際の Routing 設定を以下のように表現できていました。

const routes = <Router history={browserHistory}>

<Route path="/" component={WrapperComponent}>
<Route path="articles" component={Articles} />
<Route path="articles/:articleId" component={Article} />
</Route>
</Router>

上記の場合、WrapperComponent 内で props.children をレンダリングするようにしておくことで、URL /articles をレンダリングする場合でも、/articles/hoge をレンダリングする場合でも、WrapperComponent が常にレンダリングされます。つまり Web ページのグローバルヘッダやナビゲーション、フッタといった共通パーツとして利用できました。

Route Component の入れ子構造と URL の入れ子構造が対応しているので僕にとってはこのほうが直感的でした。

ただ、WrapperComponent の情報を、子要素にあたる Articles および Article に渡す場合、多少スマートではない方法が必要でした。

render() {

let childrenWithProps = React.Children.map(this.props.children, child =>
React.cloneElement((child as React.ReactElement<any>), {
myProfile: this.state.myProfile,
// こうしておくと下位の Componentは this.props.myProfile を利用できる
}))

return <div id="wrapper">{childrenWithProps}</div>
}

v4 では、Route を入れ子にして利用する用法がなくなりました。Migration の一例として次のようにします。


router.tsx

const routes = <BrowserRouter>

<Route path="/" component={WrapperComponent} />
</BrowserRouter>

BrowserRouter の配下にはこれまで Wrapper として使用していた Component のみを記述します。


WrapperComponent.tsx

<header>FOO</header>

<main>
<Switch>
<Route exact={true} path="/articles" render={props => (
<Articles {...props} myProfile={this.state.myProfile} />
)} />
<Route exact={true} path="/articles/:articleId" render={props => (
<Article {...props} myProfile={this.state.myProfile} />
)} />
</Switch>
</main>
<footer>BAR</footer>

WrapperComponent 側で、子要素としたい Component 群 の Routing 設定を行います。このとき、props に受け渡したい追加の情報がなければ render 属性 ではなく component 属性 に Component を指定するだけで構いません。

これで動いています... がベターな方法かどうかはじつはあまりよく分かっていません。


location について

v3 までは URL の クエリパラメータの情報は this.props.location.query で受け取ることができました。query は Object です。

v4 では this.props.location.search に文字列として格納されるよう変更されています。

https://example.com/home?foo=bar&ham=egg の場合、search の値は ?foo=bar&ham=egg です(先頭のエクスクラメーションマークに一応注意)。

したがって クエリパラメータの値を利用するには自分で Parse する必要があります。Parse には qs を利用するのが簡便でしょう。

import * as qs from "qs"

let query = qs.parse(this.props.location.search.substr(1))


その他、props.paramsprops.match.params に変更になるなどかなり Breaking Changes が入っています。アプリケーションの規模によっては Migration を断念するレベルと思います。v3 の時点で特に困っていなければアップデートするかどうかの判断を行ってもよいと思います。

ちなみに、v4 対応の TypeScript 用型定義ファイルが公開されており利用できます。

yarn add @types/react-router-dom

react-router 含めてコミュニティの活動に謝意を表明していきたい。