React Router v4 からの主な移行・変更点一覧
自分が最近React Router を使用して出くわした、前バージョンのReact Router からReact Router v4 へ移行する際の変更点一覧です。
Router のヒストリ管理をするために通常のRouter コンポーネントにhistory prop の指定がなくなり、その代わりBrowserRouter を使うようになった
ヒストリ管理がBrowser Router を使用することで簡潔化されました。
BrowserRouter を使用することで、標準でHTML5 のヒストリAPI (pushState, replaceState, popstate イベント)を使ってヒストリを管理するようになります。
Web ブラウザでURL を見るとhash 値が出てくることもないので、キレイな実装にもなります。
import { Router, hashHistory } from "react-router";
/* ...... */
ReactDOM.render(
<Router history={hashHistory}>
/* ...... */
</Router>,
app);
import { BrowserRouter } from "react-router-dom";
/* ...... */
ReactDOM.render(
<BrowserRouter>
/* ...... */
</BrowserRouter>,
app);
なお、今までどおり というコンポーネント名のまま利用したい場合は以下のようにBrowserRouter をインポートして使用しましょう。
import { BrowserRouter as Router, Route } from "react-router-dom";
/* ...... */
ReactDOM.render(
<Router>
/* ...... */
</Router>
app);
React Router のヒストリ情報へのアクセス拡張としてwituRouter を使う
前バージョンまではhistory オブジェクトへのアクセスは、特に何もせずにできましたがv4 からはwithRouter を使うようになります。
(あと個人的にlocation オブジェクトへのアクセスもこれがないとだめっぽい?)
例えば、client.js で各コンポーネントのレンダリング処理ReactDOM.render
を行っている場合、withRouter を以下のように使うことでprops.history, props.location, props.match にアクセスできるようになります。
ReactDOM.render(
<Router>
<Route path="/" component={Layout}>
<IndexRoute component={SomeComponent}></IndexRoute>
/* ...... */
</Route>
</Router>,
app);
/* ...... */
import { withRouter } from "react-router-dom";
export default class Layout extends React.Component {
render() {
/* ...... */
}
}
v3 まではwithRouter を使用しなくてもprops.history, props.location, props.match 等のオブジェクトにアクセスすることができました。
v4 からでは以下のようになります。
ReactDOM.render(
<Router>
<Layout>
<Route exact path="/" component={SomeComponent}></Route>
/* ...... */
</Layout>
</Router>,
app);
/* ...... */
import { withRouter } from "react-router-dom";
class Layout extends React.Component {
render() {
/* ...... */
}
}
export default withRouter(Layout);
上記のようにすることでLayout コンポーネント及びその子コンポーネントでprops.history
, props.location
, props.match
といったオブジェクトにアクセスできるようになります。
確認するには対象のコンポーネントでconsole.log(this.props)
とデバッグプリントするとわかりやすいです。
IndexRoute の廃止
子コンポーネントに対するデフォルトの親コンポーネントを指定するIndexRoute が廃止されました。
代わりにRoute コンポーネントに対してexact キーワードを使って対処します。
ReactDOM.render(
/* ...... */
<Route path="/" component={ParentComponent}>
<IndexRoute component={IndexComponent} ......></IndexRoute>
<Route path="child" component={ChildComponent} ......></Route>
</Route>
/* ...... */
app);
ReactDOM.render(
/* ...... */
<Route exact path="/" component={IndexComponent} ......></Route>
<Route path="/child" component={ChildComponent} ......></Route>
/* ...... */
app);
Router、Route、IndexRoute コンポーネントの親子関係が変更になった
⊃ ⊃ / というコンポーネントの親子関係が変更になりました。
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={ParentComponent}>
<IndexRoute component={IndexComponent}></IndexRoute>
<Route path="child" name="settings" component={ChildComponent}></Route>
</Route>
</Router>,
app);
これは先程紹介したexact キーワードと組み合わせて次のような構造に変わります。
ReactDOM.render(
<Router>
<ParentComponent>
<Route exact path="/" component={IndexComponent}></Route>
<Route path="/child" name="archives" component={ChildComponent}></Route>
</ParentComponent>
</Router>,
app);
query string が格納されるprops.location.query の廃止
query string の値が格納されるprops.location.query が廃止となりました。
以下はhttp://foo.example.com/component?foo=aaa&bar=bbb
のようなURL にアクセスした時のquery string 解析の例です。
export default class SomeComponent extends React.Component {
/* ...... */
render() {
const query = this.props.location;
console.log(query.foo); // -> aaa
console.log(query.bar); // -> bbb
/* ...... */
}
}
v4 では廃止されてしまったため、代わりの方法を使わなければなりません。
以下はURLSearchParams を使用した場合の例です。
export default class SomeComponent extends React.Component {
/* ...... */
render() {
const query = new URLSearchParams(this.props.location.search);
console.log(query.get("foo")); // -> aaa
console.log(query.get("bar")); // -> bbb
/* ...... */
}
}
Link が現在のURL と一致した時に使用するNavLink
NavLink はLink が現在のURL と一致した時にstyle 系の属性を適用するための特殊なLink コンポーネントです。
例えば代表的なものとしてはactiveClassName を使って現在のURL とLink が一致した時に適用するclass (style)を指定することができますが、v3 まではLink コンポーネントで利用することができました。
しかしv4 からはLink コンポーネントでは使用することができず、activeClassName を使う場合はNavLink を使う必要があります。
export default class SomeComponent extends React.Component {
/* ...... */
render() {
return (
<Link to="some_link" activeClassName="selected" ......>some_link</Link>
<Link to="some_another_link" activeClassName="selected" ......>some_another_link</Link>
);
}
}
export default class SomeComponent extends React.Component {
/* ...... */
render() {
return (
<NavLink to="some_link" activeClassName="selected" ......>some_link</Link>
<NavLink to="some_another_link" activeClassName="selected" ......>some_another_link</Link>
);
}
}
注意点として、URL にクエリストリングが使われていた場合は正しく動作しないようです。
これはReact Router v4 では一貫してquery string については処理を行わなくなったためです。
- NavLink active class not work with query string #5379
Route コンポーネントのpath に正規表現の利用が可能
Route コンポーネントのto 表記に正規表現が可能になりました。
例えば以下のようにすることで、http://some.example.com/contents/foo
にもhttp://foo.example.com/contents/bar
にもhttp://some.example.com/contents
にもマッチする条件を記載することができるようになりました。
ReactDOM.render(
/* ...... */
<Route path="/contents(/:article)?" name="contents" component={Contents}></Route>
/* ...... */
app);
参考
-
REACT TRAINING / REACT ROUTER
-
React JS Tutorials
-
react-routerのページ遷移をhandleで行う時にはwithRouterを使う
-
Migrating from v2/v3 to v4