2018/04/13更新

[フロントエンド] React Routerで、子要素でもhistory.pushを使いたい(withRouterの利用)

このエントリーをはてなブックマークに追加            

こんにちは、@yoheiMuneです。
React.jsの任意のコンポーネントから、React Routerのmatchlocationhistoryを使えるようにする方法を、ブログに書きたいと思います。



目次




対象のバージョン

React Routerのバージョンは、v4以降が対象です。v3以前とはかなり違うので、対象バージョンにご注意ください。



何が問題か

例えば、以下のルーティングがあるとします。
import React, { Component } from 'react'
import { BrowserRouter as Router, Route, Switch } from "react-router-dom"
import Thread from './Thread'
import Todo from './Todo'

class AppRouter extends Component {
  render() {
    return (
      <Router>
        <Switch>
          <Route path="/thread" component={Thread}/>
          <Route path="/todo" component={Todo}/>
        </Switch>
      </Router>
    )
  }
}
export default AppRouter
この場合、ThreadTodoのルーティング対象のコンポーネント(=ReactRouterのルーティングに指定するコンポーネント)からは、this.props.historyなど、ReactRouterの機能にアクセスすることが可能です。
// Thread.js
import React, { Component } from 'react'
class Thread extends Component {
  doSomething() {
    // React Routerの機能にアクセスできる.
    this.props.history.push('/todo')
  }
}
しかし、ルーティング対象でないコンポーネント(=ルーティング対象のコンポーネントの子要素など、以下の例ではDrawer.js)の場合、this.props.historyでアクセスできません。
// Drawer.js
import React, { Component } from 'react'
class Drawer extends Component { 
  doSomething() {
    // 子要素からは、React Routerの機能にアクセスできない.
    this.props.history.push('/todo')   // ERROR : this.props.history が undefined
  }
}
export default Drawer
// Todo.js
import React, { Component } from 'react'
import Drawer from './Drawer'
class Todo extends Component {
  render() {
    // 子要素として利用
    return <Drawer/>
  }
}
今回は、Drawer.jsのようなサブコンポーネントでもReactRouterの機能を使えるようにしたい、という話です。



子コンポーネントでもReact Routerの機能を使う

対処法はいくつかありますが、withRouterを使うとシンプルに実装できます。
Drawer.jsを以下のように改造します。
// Drawer.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'         // [変更点1] importを追加
import { withRouter } from 'react-router'  // [変更点2] importを追加

class Drawer extends Component { 

  // [変更点3] propTypesの定義追加
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  }

  doSomething() {
    // React Routerの機能にアクセスできた!
    this.props.history.push('/todo')
  }
}

// [変更点4] withRouterでラッピング
export default withRouter(Drawer)
変更点1,2でimportするモジュールは、npm installでインストールしてください。
上記の変更点1〜4を実装することで、Drawer.jsの中でもmatchlocationhistoryにアクセスできるようになります。便利ですね。



参照ドキュメント

公式にも説明がありますので、そちらも合わせてご確認ください。

https://reacttraining.com/react-router/web/api/withRouter



最後に

React Routerの機能は奥が深くて、学んでいて楽しいですね。しっかりと実世界に生かしていきたいところです。

最後になりますが本ブログでは、フロントエンド、Swift、PHP、Node.js、Python、Java、Linux、インフラ、Go言語、機械学習、などの技術トピックを発信をしていきます。「プログラミングで困ったその時の、解決の糸口に!」そんな目標でブログを書き続けています。ぜひ、本ブログのRSSTwitterをフォローして貰えたら嬉しいです ^ ^

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

[取り組み] フロントエンドでコーディングスピードをアップさせる6つの方法!と思って書いてたら30個も書いちゃった。
[フロントエンド] フロントエンドの入社試験99問!難しいですよ〜w。
[フロントエンド] Webページを表示するテストの際に、通信速度を3Gに制限して表示してみよう
[フロントエンド] スマホ実機でのデバッグ手段を増やす!Macのプロキシを利用して、通信内容を確認する。
[フロントエンド] Chrome 35 Beta の変更点。Touch制御、新しいJavaScript機能、プレフィックスなしのShadowDOM
[フロントエンド]複数アカウントでのテストには、Chromeのユーザー管理を使って、Cookieを切り替えると便利
[フロントエンド] Chrome36βが出た。変更点など。element.animate、HTML Imports、Object.observe、他。
RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。