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
出るか警告
でました。いざ修正
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に渡してあげます。
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()とかすることになった
について修正します。もともとのソースは、
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');の修正。
が必要になります。
修正すると、以下のようになります。
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付きで渡したい場合は、
<Link to="/portal" query={{q: "hoge"}} style={{paddingRight: "5px"}}>ポータル</Link>
が、
<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はそのままでした。関係ないのかな。