react-router v2.xとcontext

# react-router また仕様変更
## react-router v1.xからv2.xへのバージョンアップ
v0.xからv1.xにあがった時点で色々ありましたが、v2.xでもまた色々あるみたいです。
本家サイトに、[v2.0.0 Upgrade Guide](https://github.com/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md)がありました。

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

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

というところです。

## context
以前はReactの公式ページでは確かcontextについて記述がなかったのですが、いつの頃からか記述が追加されました。
[https://facebook.github.io/react/docs/context.html](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の値を参照 */}
    );
  }
});
```

### 多用しないでね。
[公式ページ](https://facebook.github.io/react/docs/context.html)には注意書きがあります。適当に訳してみました。

> **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
```

### 出るか警告
<img width="1148" alt="スクリーンショット 2016-02-11 10.04.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/89819/759192a4-5866-868c-3042-5f1ca6a15da9.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](https://github.com/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md#using-browser-html5-pushstate-history)
[Using Custom Histories](https://github.com/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md#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](https://github.com/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md#using-custom-histories)
にあるように、引数も変更されています。

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

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

引数の変更についてはLinkコンポーネントでも同様です。
公式サイトの、
[\<Link to\>, onEnter, and isActive use location descriptors](https://github.com/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md#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/ReactTraining/react-router/blob/v2.0.0/upgrade-guides/v2.0.0.md#upgrade-automatically-with-codemods)
それによると、
[https://github.com/rackt/rackt-codemod](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
```
とでました。１ファイルpushStateがpushに修正されました。多階層には対応しているのだろうか。。
また、react-router/deprecate-context-history.jsも実行してみましたが、
mixinsはそのままでした。関係ないのかな。

# サンプルソース
[https://github.com/kunitak/react-router-1to2/tree/v2](https://github.com/kunitak/react-router-1to2/tree/v2)