今からはじめるReact.js〜画面遷移〜

前回→[今からはじめるReact.js〜サーバーとの通信〜](http://qiita.com/kuniken/items/bc20fd1e907d91b641d6)

# react-router
※(2016/01/19)react-routerのバージョンは1.xを前提にしています。
※(2016/02/18)2.xネタは[こちら](http://qiita.com/kuniken/items/b8777c8342fe1b4ff727)
なんらかのサービスを作成した場合、製品カテゴリーごとに画面が存在するとか、メインの画面とは別にに設定画面があるとか、会員制サイトであればログイン画面があって、ログインしたら、ポータル画面に遷移したり、といったように、複数の画面が存在して、それらの画面に行き来できる仕掛けが必要になってきます。
react-routerというライブラリは画面遷移を管理してくれるライブラリです。

## インストール
`npm install react-router --save`

npmのバージョンが3以上の場合は、historyもインストールします。
`npm install history --save`

react-routerとhistoryはバージョンの対応関係があるため、
npm install時にエラーが出力される場合があります。その場合は、
`npm install history@1.17.0 --save`
など、バージョンを指定してインストールしてください。

開発元の解説でreact-routerを使わなかった場合と使った場合とでどのように画面遷移を行うかの記述を参照することができます。
https://github.com/rackt/react-router/blob/master/docs/Introduction.md

## 実装
### 複数画面の用意
画面を複数用意しましょう。
まずユーザーリストがbody.jsxで実装されていますが、UserBox、User、UserList、UserFormの定義はuserbox.jsxとして別のファイルに分けてしまいましょう。

```js:body.jsx
var React = require('react');

//ボディの定義
var Body = React.createClass({
  render: function(){
    return (
      <h1>ポータル</h1>
    );
  }
});

module.exports = Body;
```

```js:userbox.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var request = require('superagent');

//フォームとリストを一つにしたもの
var UserBox = React.createClass({
  getInitialState:function(){
    return {userData:[]};
  },
：
：
：
    );
  }
});

module.exports = UserBox;
```
UserBoxをmodule.exportsに代入するのを忘れないようにします。

### react-routerの実装
#### やりたいこと
index.jsでbody.jsxやuserbox.jsxをそれぞれ別のwebページとしてアクセスできるように、react-routerの実装を行なっていきます。

やりたいことは、以下の通りとします。
・トップページをログイン画面とする。
・ログインボタンをクリックしたら、ポータル画面に遷移する。
・ポータルページにログアウトボタンとポータル、ユーザーリスト画面に遷移するためのリンクを貼る。
・ヘッダとフッタは画面遷移してもそのまま表示させるようにする。

図にすると以下な感じです。

![main.png](https://qiita-image-store.s3.amazonaws.com/0/89819/cb028560-08b3-190c-6134-7818c327bfaf.png)

#### index.jsの修正
必要なコンポーネントをインポートします。

まずはreact-routerのインポート。

```js:index.js
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var IndexRoute = ReactRouter.IndexRoute;
var History = ReactRouter.History;
```
で、userbox.jsxのインポート。

```js:index.js
var UserBox = require('./views/userbox.jsx');
```

Indexは以下の通り。ここから全ての画面に遷移できるようにします。

```js:index.js
var Index = React.createClass({
  render: function () {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
});
```
this.props.childrenの中に画面が渡されます。

では、TopとMainコンポーネントを定義します。

```js:index.js
var Top = React.createClass({
  handleSubmit:function(){
    //ログイン＆ポータル画面へ
  },
  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>
    );
  }
});
```
ログインボタンをクリックしたら、handleSubmitイベントが実行されるようにしました。
handleSubmitでポータル画面に遷移されるようにします。中身は後で実装します。

次に、Main。

```js:index.js
var Main = React.createClass({
  render:function(){
    return (
      <div>
        <Header/>
        <div className="main">
          {this.props.children}
        </div>
        <Footer/>
      </div>
    );
  }
});
```
MainでHeaderコンポーネントとFooterコンポーネントを定義し、
`<div className="main">`の中で表示コンポーネントを切り替えるようにします。

次にページの遷移経路を定義します。

```js:index.js
var Routes = (
  <Route path="/" component={Index}>
    <IndexRoute component={Top}/>
    <Route path="/top" component={Top}/>
    <Route path="/portal" component={Main}>
      <IndexRoute component={Body}/>
      <Route path="/userbox" component={UserBox}/>
    </Route>
  </Route>
);
```

react-routerのRouteコンポーネントは上記のように入れ子にすることができます。
IndexRouteは、入れ子のうち、デフォルトとして表示したいコンポーネントを指定したい場合に指定します。
上記であれば、インデックスページにアクセスした際に、Indexコンポーネント内でTopコンポーネントをデフォルトとして表示することになります。
UserBoxはポータル画面から遷移する画面なので、Mainコンポーネントの入れ子として定義しています。

ここまできたら、TopコンポーネントのhandleSubmitの中身を実装します。
（ログイン処理とログアウト処理自体は今回は割愛します）

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

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

    //ポータル画面へ
    this.history.pushState(null, '/portal');
  },
```
インポートしていた、Historyを使用します。
mixinsでHistoryを指定しています。

mixinsを指定すると、指定したコンポーネントのファンクションをコンポーネントに合成することができます。
実際に、Historyのソースを覗いてみると、

```js:node_modules/react-router/lib/History.js
'use strict';

exports.__esModule = true;

var _PropTypes = require('./PropTypes');

var History = {

  contextTypes: { history: _PropTypes.history },

  componentWillMount: function componentWillMount() {
    this.history = this.context.history;
  }

};

exports['default'] = History;
module.exports = exports['default'];
```
contextTypesとcomponentWillMountが宣言されており、
それぞれ、historyに関する定義および処理が記述されています。

従って、`mixins: [ History ]`によって、
`this.history.pushState(null, '/portal');`
が実行できるようになります。
上記であれば、pushStateによって、/portalに遷移し、新しく履歴を追加します。

History.jsはブラウザの遷移履歴を管理するライブラリです。
HTML5であれば、
https://developer.mozilla.org/ja/docs/Web/Guide/DOM/Manipulating_the_browser_history
のようにAPIとして用意されています。詳しくは、またの機会に。。

最後にrenderでRoutesを指定します。

```js:index.js
ReactDOM.render(
  <Router>{Routes}</Router>,
  document.getElementById('content')
);
```

#### header.jsxの修正
ヘッダに、ポータルへのリンク、ユーザーリストへのリンク、ログアウトボタンを実装します。

以下のようになります。

```js:header.jsx
var React = require('react');
var ReactRouter = require('react-router');
var History = ReactRouter.History;
var Link = ReactRouter.Link;

//ヘッダの定義
var Header = React.createClass({
  mixins: [ History ],
  
  handleClick: function(e){
  	/* ログアウト処理 */
    
    //ログイン画面へ
    this.history.pushState(null, '/');
  },
  render: function(){
    return (
      <header>
        <div style={{position:"absolute", margin: "-15px 0px"}}>
          <h1>ヘッダです</h1>
        </div>
        <div style={{position:"relative", textAlign:"right", paddingTop: "30px"}}>
          <Link to="/portal" style={{paddingRight: "5px"}}>ポータル</Link>
          <Link to="/userbox" style={{paddingRight: "5px"}}>ユーザーリスト</Link>
          <button onClick={this.handleClick}>ログアウト</button>
        </div>
        <hr/>
      </header>
    );
  }
});

module.exports = Header;
```
index.jsxでやったことの繰り返しですね。

ここまできたら、実際に動かしてみましょう。

# サンプルソース
https://github.com/kunitak/react-tutorial/tree/day8

次回→[今からはじめるReact.js〜Flux〜](http://qiita.com/kuniken/items/b532d4fa980b53fd6bd5)