Cloud9環境でReactの検証⑤ -Ajax、state管理-

<b>2016/9/17 関連記事情報を追加しました。</b>

本章ではReactでのstate管理について説明します。
ただ、state管理については、現在reduxの使用がメジャーとなっているようで、
Reactでstate管理する場面は少ないのかもしれません。

1. immutableとmutable
-----
まず言葉の定義から。

* immutable(イミュータブル)
作成後にその状態を変えることができないオブジェクトを指します。
4章までで作成したサンプルでは、1度しかレンダリングが行われないように作成されているため、イミュータブルなアプリといえます。


* mutable(ミュータブル) 
作成後でも状態の変更が可能なオブジェクトを指します。
本章で作成するアプリは、更新を検知するようにし、自身を再びレンダリングするように作成するため、ミュータブルなアプリといえます。

2. state管理
-----
今回は、別サーバ上のPHPで作成されたAPIをAjaxでたたき、取得したデータをReactでstate管理・レンダリングするような構成となります。画面表示結果は4章までと変わりありません。

```javascript:app.js
/*global $*/

import React from "react";
import ReactDOM from "react-dom";

var UserBox = React.createClass({
    render: function(){
        return(
            <div classNmae="userBox">
                <ul>
                    <li>No: {this.props.no}</li>
                    <li>Name: {this.props.name}</li>
                    <li>Message: {this.props.message}</li>
                    <li>Age: {this.props.age}</li>
                </ul>
            </div>
        );
    }
});

var UserData = React.createClass({
    loadUserData: function(){
        $.ajax({
            url: this.props.url,
            dataType: 'json',
            cache: false,
            success: function(data){
               this.setState({
                   data: data
               });
            }.bind(this)
        });
    },
    getInitialState: function(){
        return {data: []};
    },
    componentWillMount: function(){
        this.loadUserData();
        setInterval(this.loadUserData, this.props.pollInterval);
    },
    render: function(){
        var usermap = this.state.data.map(function(user) {
            var age = 25;
            var key = user.no;
            return (
                <UserBox no={user.no} name={user.name} message={user.message} age={age} key={key} />
            );
        });
        return (
            <div className="userData">
                {usermap}
            </div>
        );
    }
});

ReactDOM.render(
    <UserData url="https://your-api-project.c9users.io/server.php" pollInterval={1000} />,
    document.getElementById('content')
);
```

```getInitialState``` はコンポーネントライフサイクルで一度だけ呼び出され、stateの初期値を設定します。ここでは、```data``` を初期化しているような形となります。
```componentWillMount``` はコンポーネントがDOMツリーに追加される前に一度だけ呼びだされます。ここでは、```this.loadUserData()```(※AjaxでAPIをたたく処理)と、```setInterval```(※1000ミリ秒毎に```this.loadUserData()```を呼び出す処理)が実行されています。

```componentWillMount``` で2回```this.loadUserData()```が呼ばれているように見えますが、```setInterval```内の```this.loadUserData()```は、初回レンダリングまでに1000ミリ秒かかってしまうため、```setInterval```の外で一度```this.loadUserData()```を呼んでやる必要があります。

余談ですが、ReactにはAngular2のhttpモジュールのようなAjax機能は存在しないため、何かしら用意してやる必要性があります。この例ではjqueryを使用しています。
（上記のような色々と欠けている機能を補完しないといけないところが、Reactの設計が大変だと言われる一端かもしれないですね。）

3. テスト用API
-----
2節でReact側の実装は完了しましたが、これだけでは本章のアプリは動作しません。```https://your-api-project.c9users.io/server.php``` を別サーバで実装する必要があります。（別に同じサーバでもいいんですけどね、、）

まず、Cloud9のページにアクセスし、自身のアカウントでログインする。次に「Create a new workspace」をクリックします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/91976/abcb021a-e61d-e6ca-c7b4-127a0ba37574.png" width="400px">

作成画面では、下記の例のように好みのWorkspace名を記載します。今回は他機能からAPIをたたく必要があるため、「Public」を選択しておいてください。また、APIはPHPで作成するので、「PHP」を選択しておいてください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/91976/f2582ab3-3360-098a-f4c9-00b00818f7b1.png" width="400px">

WorkSpaceが立ちあがったら、「api-project」配下にserver.phpを作成し、中に下記コードを記載します。

```php:server.php
<?php

$value = array(
	array('no'=> 1, 'name' => "Taro", 'message' => "Hello"),
	array('no'=> 2, 'name' => "Jiro", 'message' => "Good Bye"),
	array('no'=> 3, 'name' => "Kumi", 'message' => "Thank you"),
);

header("Content-Type: application/json; charset=UTF-8");
header("X-Content-Type-Options: nosniff");
header("Access-Control-Allow-Origin: *");

echo json_encode(
	$value,
	JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
);
```

コードを保存したら、server.php > Run でApacheを起動します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/91976/f56fee70-a1a9-332a-8aed-b8c22566cc9c.png" width="200px">

Apache起動後に下記のように表示が出るので、表示されたURLをapp.jsの```render```内の```url```に記載してください。

```bash:cloud9
Starting Apache httpd, serving https://your-api-project.c9users.io/server.php.
Started apache2
```

3. 確認
-----
ここまで完了したら、react-studyのindex.htmlをRunして、表示を確認してください。下図のように表示されていたら成功です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/91976/53149103-058e-f4b2-bce9-e05294e1347a.png" width="400px">

最後にミュータブルになっているか確認してみましょう。api-projectのserver.phpのjson項目を書き換えてみてください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/91976/e7afeb00-1021-125f-f918-c67e6bd558cb.png" width="400px">

ページリロードをしていないにもかかわらず、index.htmlの表示が自動で変わっていることが確認できます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/91976/22237ee9-1c3b-3732-eb07-b0ad21e95920.png" width="400px">

4. 引用
-----
チュートリアル | React
https://facebook.github.io/react/docs/tutorial-ja-JP.html

React.jsのComponent Lifecycle
http://qiita.com/koba04/items/66e9c5be8f2e31f28461

5. 関連記事
-----
[cloud9環境でReactの検証① -導入-](http://qiita.com/tomine3/items/e6390cd6691a95a8ea53)
[cloud9環境でReactの検証② -コンポーネント-](http://qiita.com/tomine3/items/0e5e8965668051157901)
[cloud9環境でReactの検証③ -変数の扱い-](http://qiita.com/tomine3/items/1f6212a3a98f095ebabc)
[cloud9環境でReactの検証④ -ループ-](http://qiita.com/tomine3/items/ccffb50fad8d7e890379)