Help us understand the problem. What is going on with this article?

react-router v2.xとcontext

More than 1 year has passed since last update.

react-router また仕様変更

react-router v1.xからv2.xへのバージョンアップ

v0.xからv1.xにあがった時点で色々ありましたが、v2.xでもまた色々あるみたいです。
本家サイトに、v2.0.0 Upgrade Guideがありました。

それによると、大きな変更点は、

  • historyをrouter定義時に必ず指定することになった
  • コンポーネント定義時にmixinsでHistoryを指定しなくなった
  • contextでrouterを使い回すことになった
  • なのでpushStateなんかはcontext.router.push()とかすることになった
  • 今までの実装のままだとコンソールに警告出る、で、次のVerから使えなくなる

というところです。

context

以前はReactの公式ページでは確かcontextについて記述がなかったのですが、いつの頃からか記述が追加されました。
https://facebook.github.io/react/docs/context.html
githubの履歴を調べたら最初のコミットが2015年10月10日になっていました。

公式な記述が追加されたってことは、条件付きだったとしても使っていいよ、ってことですよね。

contextの便利なところ

contextを使わない場合、親から子に値を渡す時は、以下のように、

var Parent = React.createClass({
  :
  :
  render: function(){
    return (
      <Child param={this.state.param} />
    );
  }
});

var Child = React.createClass({
  render: function(){
    return (
      <div>{this.props.param}</div>
    );
  }
});

stateとpropsを使って、受け渡しをする必要がありました。

これをcontextに置き換えた場合、子供に値を渡してあげる必要がありません。

var Parent = React.createClass({
  getChildContext: function() {
    return {param: ''};
  },
  :
  :
  render: function(){
    return (
      <Child /> {/* contextの場合、値を渡す必要なし */}
    );
  }
});

var Child = React.createClass({
  render: function(){
    return (
      <div>{this.context.param}</div> {/* contextの値を参照 */}
    );
  }
});

多用しないでね。

公式ページには注意書きがあります。適当に訳してみました。

Note:
contextは、先進的かつ実験的な機能です。APIは、将来のリリースで変更する可能性があります。

ほとんどのアプリでは、contextを使用する必要はありません。あなたがReact.jsを使い始めの場合は特に必要ないでしょう。contextを使用すると、データの流れが不明瞭になりますので、コードを理解するのが難しくなります。あなたのアプリでcontextを使うということは、グローバル変数としてstateを受け渡すのと同様のことなのです。

contextを使用する必要がある場合、慎重に行ってください

アプリを実装するかライブラリを実装するかによらず、contextの使用は小さな領域に分離し、直接APIを触らないように実装してみてください。それができれば、APIが変更されてもアップグレード作業がより簡単にできます。

とのことです。おかしかったらコメントください。

redux使ってたらあんまり使うことはなさそうな気もします。

react-router v1.xベースのソースをv2.xベースに修正してみる

今日から始めるシリーズで使ってるソースを使って、v2.xベースに修正してみます。

npm インストール

npmを最新にして、react-router、historyを2.0.0にしました。

$ sudo npm install npm -g
$ npm install react-router@2.0.0 history@2.0.0 --save

出るか警告

スクリーンショット 2016-02-11 10.04.20.png
でました。

いざ修正

1個目の警告は、

  • historyをrouter定義時に必ず指定することになった

これのせいですね。公式サイトだと、
[https://github.com/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md#no-default-history)
Using Browser (HTML5 pushState) History
Using Custom Histories
に詳しい記述があります。

ということで、historyをインポートします。で、Routerに渡してあげます。

index.js
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;

var hashHistory = ReactRouter.hashHistory;
//browserHistoryの場合
//var browserHistory = ReactRouter.browserHistory;

:

ReactDOM.render(
  <Router history={hashHistory}>{Routes}</Router>,
  document.getElementById('content')
);

次に、2個目以降の警告の部分、

  • コンポーネント定義時にmixinsでHistoryを指定しなくなった
  • contextでrouterを使い回すことになった
  • なのでpushStateなんかはcontext.router.push()とかすることになった

について修正します。もともとのソースは、

index.js
var Top = React.createClass({
  mixins: [ History ],

  handleSubmit:function(e){
    e.preventDefault();
    /* ログイン処理 */

    //ポータルページへ
    this.history.pushState(null, '/portal');
  },
  render:function(){
    return (
      <div>
        <div className="main">
          <h1>ログイン</h1>
          <form onSubmit={this.handleSubmit}>
            <input placeholder="userid"/>
            <input placeholder="password"/>
            <div style={{textAlign:"center"}}>
              <button type="submit">ログイン</button>
            </div>
          </form>
        </div>
      </div>
    );
  }
});

こんな感じでした。
ので、

  • contextの定義。
  • mixins: [History]を削除。
  • this.history.pushState(null, '/portal');の修正。

が必要になります。
修正すると、以下のようになります。

index.js
var Top = React.createClass({
  contextTypes: {
    router: React.PropTypes.object.isRequired
  },
  handleSubmit:function(e){
    e.preventDefault();
    /* ログイン処理 */

    //ポータルページへ
    this.context.router.push('/portal'); //!!
  },
  render:function(){
    return (
      <div>
        <div className="main">
          <h1>ログイン</h1>
          <form onSubmit={this.handleSubmit}>
            <input placeholder="userid"/>
            <input placeholder="password"/>
            <div style={{textAlign:"center"}}>
              <button type="submit">ログイン</button>
            </div>
          </form>
        </div>
      </div>
    );
  }
});

上記の修正の、

this.context.router.push('/portal');

についてですが、pushState、replaceStateといった〜State()は、公式サイトの、
Programmatic Navigation
にあるように、引数も変更されています。

path以外でqueryとかを指定したい場合は、オブジェクトで渡す必要があります。

this.context.router.push({pathname: '/portal', query: '', state: ''});

引数の変更についてはLinkコンポーネントでも同様です。
公式サイトの、
<Link to>, onEnter, and isActive use location descriptors
によると、query付きで渡したい場合は、

V1.x
<Link to="/portal" query={{q: "hoge"}} style={{paddingRight: "5px"}}>ポータル</Link>

が、

V2.x
<Link to={{pathname: "/portal", query: {q: "hoge"}}} style={{paddingRight: "5px"}}>ポータル</Link>

になります。

んー

contextが変更する可能性があるというなら、結局自分でmixinを作ったりとかラッパーを作ったりしないといけなくなるのではなかろうか。

移行ツール

公式サイトでは、移行ツールを使った修正方法について案内があります。
Upgrade Automatically with Codemods
それによると、
https://github.com/rackt/rackt-codemod
に書いてある方法でインストールし、ツールを動かしてと書いてあります。

$ sudo npm install -g jscodeshift
$ npm install rackt-codemod

ツールを動かしてみます。以下な感じ。

$ jscodeshift -t node_modules/rackt-codemod/transforms/history/deprecate-pushState-replaceState.js ./client/scripts/*

結果を見たら、

Processing 5 files...
Spawning 1 workers with 5 files each...
All done.
Results: 0 errors 4 unmodified 0 skipped 1 ok
Time elapsed: 1.99 seconds

とでました。1ファイルpushStateがpushに修正されました。多階層には対応しているのだろうか。。
また、react-router/deprecate-context-history.jsも実行してみましたが、
mixinsはそのままでした。関係ないのかな。

サンプルソース

https://github.com/kunitak/react-router-1to2/tree/v2

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away