react-railsを使ってReactのTutorialをやってみる


# はじめに
[react-rails](https://github.com/reactjs/react-rails)という、ReactをAsset Pipelineに乗せて使えるようにしてくれるruby gemsがある。

この記事では、これを使用して[Reactの公式tutorial](https://facebook.github.io/react/docs/tutorial.html)を進めていく。

公式のtutorialではサーバ側はすでに実装済みとして進んでいる。
今回はせっかくなのでtutorialを進める中でサーバ側も実装していくことにする。

ちなみに使ってみた個人的な感想としては、RailsのAsset Pipelineに乗せるならすごく順当かなー、という感じ。
browserifyを使いたい。bowerもオワコンと呼ばれてしまう。そんなこの時代。
Asset Pipelineは捨てたい感じがあるけど、既存のアプリケーションを徐々にReactコンポーネント化していく、みたいな時には良いと思う。

## 方針

- きまぐれで、必要そうなところはReactの説明する。けど英訳とかはしない。
- 章立ては公式Tutorialと揃えているので、併せて読み進めると理解しやすいはず。
- 全くReactの概念が分からない人は、[一人React.js Advent Calendar 2014](http://qiita.com/advent-calendar/2014/reactjs)という素晴らしい記事がある。読んでおくときっと理解しやすい。

## 元となっている資料
- [react-rails](https://github.com/reactjs/react-rails)
- [Reactの公式tutorial](https://facebook.github.io/react/docs/tutorial.html)

## おすすめの記事
- [一人React.js Advent Calendar 2014](http://qiita.com/advent-calendar/2014/reactjs)

## 環境
- ruby 2.2.0
- rails 4.2.1
- react-rails 1.0.0

## github

https://github.com/joe-re/react-rails-tutorial

コミットの単位を章立てに合わせているので、tigとかで差分を見ながら進めるとやりやすいかも知れない。

# tutorial

## 下準備

rails newでプロジェクトを作成。

```bash
$ rails new react-rails-tutorial
$ cd react-rails-tutorial
```

react-railsを追加。

```bash
$ vi Gemfile
# ...省略
gem 'react-rails', '~> 1.0' # 追加する

$ bundle install
```

react-railsには[reactを使えるようによしなにしてくれるgenerator](https://github.com/reactjs/react-rails#installation)が用意されている。叩く。

```bash
$ rails g react:install
```

react-railsにはReactのビルドオプションを設定することができる。tutorialではいずれも指定の必要はないので飛ばす。[参考](https://github.com/reactjs/react-rails#reactjs-builds)

## [Your first component](https://facebook.github.io/react/docs/tutorial.html#your-first-component)

react-railsにはComponent定義を生成してくれるgeneratorがある。これを叩いて最初のComponentを作る。

```bash
rails g react:component CommentBox
```

`app/assets/javascripts/components/comment_box.js.jsx`が作られる。

中身を書き変える。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        Hello, world! I am a CommentBox.
      </div>
    );
  }
});
```

### Railsのcontrollerとviewを作成

generatorを使って、Controllerを作成する。

```
$ rails g controller comments
```

viewを作成する。

```ruby:app/views/comments/index.html.erb
<%= react_component('CommentBox') %>
```

react-railsをインストールすると、react_componentというhelperメソッドが追加される。
この第一引数に、ここで使うReact Componentを文字列で渡すと、ここにrenderしてくれる。


ルーティングを設定する。

```ruby:config/routes.rb 
root 'comments#index'
```

`rails server`でサーバを立ち上げて、`http://localhost:3000`にアクセスしてみる。
 「Hello, world! I am a CommentBox.」と表示されればok。

## [Composing components](https://facebook.github.io/react/docs/tutorial.html#composing-components)

`comment_box.js.jsx`に子Componentの定義を追加する。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
// ...省略

var CommentList = React.createClass({
  render: function() {
    return (
      <div className="commentList">
        Hello, world! I am a CommentList.
      </div>
    );
  }
});

var CommentForm = React.createClass({
  render: function() {
    return (
      <div className="commentForm">
        Hello, world! I am a CommentForm.
      </div>
    );
  }
});
```

追加したモジュールを使うようにCommentBoxを修正。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList />
        <CommentForm />
      </div>
    );
  }
});

# ...省略
```

最終的はこうなる。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList />
        <CommentForm />
      </div>
    );
  }
});

var CommentList = React.createClass({
  render: function() {
    return (
      <div className="commentList">
        Hello, world! I am a CommentList.
      </div>
    );
  }
});

var CommentForm = React.createClass({
  render: function() {
    return (
      <div className="commentForm">
        Hello, world! I am a CommentForm.
      </div>
    );
  }
});
```

この時点でブラウザからアクセスしてみると、こうなっている。

![Screenshot 2015-04-28 02.32.03.png](https://qiita-image-store.s3.amazonaws.com/0/28883/9ef6f63c-b2a4-4fb1-f675-d0c88ce1ec77.png "Screenshot 2015-04-28 02.32.03.png")

### [Using props](https://facebook.github.io/react/docs/tutorial.html#using-props)

ここではReactのpropsを使って、Component間で値の受け渡しをする。

まずは`comment_box.js.jsx`に子Componentの定義を追加する。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
// ... 省略
var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        {this.props.children}
      </div>
    );
  }
});
```

JSXの中では、{}で囲うことでComponentの保持している変数にアクセスできる。
ここでは`this.props.author`と`this.props.children`にアクセスしている。
`this.props.hoge`というのがComponentで保持しているpropsへのアクセス方法で、この値は親Componentから渡される。

### [Component Properties](https://facebook.github.io/react/docs/tutorial.html#component-properties)

`comment_box.js.jsx`のCommentListを、先ほど定義したCommentを使用するように修正する。

```javascrpt:app/assets/javascripts/components/comment_box.js.jsx
// ... 省略
var CommentList = React.createClass({
  render: function() {
    return (
      <div className="commentList">
        <Comment author="Pete Hunt">This is one comment</Comment>
        <Comment author="Jordan Walke">This is *another* comment</Comment>
      </div>
    );
  }
});
// ... 省略
```

ここで`author="Pete Hunt"`と書いているところがReactのpropsの受け渡し部分。こうすると、Comment Componentでは、`this.props.author`でここで渡された値にアクセスできる。

この時点で`http://localhost:3000`にアクセスすると、こうなる。

![Screenshot 2015-04-28 02.51.12.png](https://qiita-image-store.s3.amazonaws.com/0/28883/a3f8d079-0efc-d468-9ad0-3e86fcb511db.png "Screenshot 2015-04-28 02.51.12.png")

### [Adding Markdown](https://facebook.github.io/react/docs/tutorial.html#adding-markdown)

[marked](https://github.com/chjj/marked)を使って、CommentをMarkdown形式で書けるようにする。

```bash
$ vi Gemfile
# ...省略
gem 'marked-rails' # 追加

$ bundle install
```


```javascript:app/assets/javascripts/application.js
// ...省略
//= require marked # 追加
```

Comment Componentでmarkdownを変換するように書き換える。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        {marked(this.props.children.toString())}
      </div>
    );
  }
});
```

このままだと、htmlタグがエスケープして表示される。
これはReactが僕らを危険なコードから守ってくれているからだ。

今回は信頼のおけるコードとして、レンダリングして欲しいので下記のようにする。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var Comment = React.createClass({
  render: function() {
    var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        <span dangerouslySetInnerHTML={{__html: rawMarkup}} />
      </div>
    );
  }
});
```

そうすると意図通りにレンダリングされる。

![Screenshot 2015-04-28 03.05.40.png](https://qiita-image-store.s3.amazonaws.com/0/28883/24e1b867-8d75-62c8-8aaf-88303b772611.png "Screenshot 2015-04-28 03.05.40.png")


### [Hook up the data model](https://facebook.github.io/react/docs/tutorial.html#hook-up-the-data-model)

最終的にはサーバからJSONを取得してクライアントでレンダリングするけど、とりあえず現段階ではview(erb)に埋め込んで渡す。

react-railsでは、`react_component`メソッドの第2引数にrubyのhashを渡すことで、reactのpropとしてComponentに渡せる。

```ruby:app/views/comments/index.html.erb
<%= react_component('CommentBox',
  data: [
    {author: "Pete Hunt", text: "This is one comment"},
    {author: "Jordan Walke", text: "This is *another* comment"}
  ]
) %>
```

埋め込んだdataを使うように、CommentBoxとCommentListを書き換える。

```javascrit:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.props.data} />
        <CommentForm />
      </div>
    );
  }
});

var CommentList = React.createClass({
  render: function() {
    var commentNodes = this.props.data.map(function (comment) {
      return (
        <Comment author={comment.author}>
          {comment.text}
        </Comment>
      );
    });
    return (
      <div className="commentList">
        {commentNodes}
      </div>
    );
  }
});
```

localhost:3000にアクセスして、無事ReactのComponentにdataが渡せてレンダリングされていることが確認できればok。

### [Fetching from the server](https://facebook.github.io/react/docs/tutorial.html#fetching-from-the-server)

先ほどviewに埋め込んだJSONをサーバから受け取るようにする。

まずWebAPI用のコントローラを作成。jsonを返すWebAPIなのでjavascriptやstylesheetは要らない。--no-assetを指定する。

```bash
$ rails g controller api/v1/comments --no-assets
```

Controllerを実装する。

```ruby:app/controllers/api/v1/comments_controller.rb
class Api::V1::CommentsController < ApplicationController
  def index
    @data = [
      { author: 'Pete Hunt', text: 'This is one comment' },
      { author: 'Jordan Walke', text: 'This is *another* comment' }
    ]
  end
end
```

jsonを返すviewを定義する。


```ruby:app/views/api/v1/comments/index.json.jbuilder
json.data(@data) { |d| json.extract!(d, :author, :text) }
```

routeを定義する。


```ruby:config/routes.rb
# ...省略
namespace :api, format: 'json' do
  namespace :v1  do
    resources :comments
  end
end
```

この段階で http://localhost:3000/api/v1/comments.json にアクセスしてみて、JSONが返却されればok。

CommentBoxにはこのAPIのurlを渡すようにする。

```ruby:app/views/comments/index.html.erb
<%= react_component('CommentBox', url: '/api/v1/comments.json') %>
```

まだurlを渡すようにしただけで、APIにはアクセスしていない。
現段階で`http://localhost:3000`にアクセスすると、エラーになる。

### [Reactive state](https://facebook.github.io/react/docs/tutorial.html#reactive-state)

ここでstateという概念が出てくる。
既に出てきているpropsは、これまで必ず親コンポーネントから渡されている。
propsの値は親Componentで管理するので、決してReactのComponentの中で変更してはいけない。
これを守ることで、ReactのComponentは同じpropsを渡される限りは、必ず同じレンダリング結果になるので、immutableなComponentとして扱えることになる。これ重要！

じゃあ親Componentでは値の変更はどうするの、というとこれはstateという、Componentの状態を保持するための変数に適用する。

下記の記事を読むとよく分かる。

- [React.jsのProp](http://qiita.com/koba04/items/bc13d1f42964278ae14e)
- [React.jsのState](http://qiita.com/koba04/items/63267bcc918d76ac8767)

CommentBoxにおいては、urlがpropsでWebAPIで取得したdataはstateになる。

stateは`getInitialState()`というメソッドで初期値を与えられる。
CommentBoxを、dataの初期値には空配列を与えて、CommentListには`this.state.data`を与えるように変更する。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
});
# ...省略
```

こうすると`http://localhost:3000`にアクセスした時のエラーは消える。ただしまだWebAPIにはアクセスしていないので、Commentは空になる。

#### [Updating state](https://facebook.github.io/react/docs/tutorial.html#updating-state)

ReactのComponent内で使える`componentDidMount()`というメソッドがある。これはReactのComponentが最初にrenderされる時に時に呼び出される。
このメソッド内でWebAPIにアクセスして、データを取得するように実装する。

```javascript:app/assets/javascripts/components/
var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      success: function(result) {
        this.setState({data: result.data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
});
// ....省略
```


これで/api/v1/comments.jsonにアクセスして取得したJSONを元にcomponentがrenderできるようになる。

`this.setState()`メソッドを用いてstateを変更していることに注目する。stateを変更する時は、必ず`this.setState()`メソッドで変更しなければならない。
Reactはこのメソッドを通じてstateが変更された時に、Componentを再度renderする。

このままではページを開いた時にしかrenderされないので、簡単なpollingを実装してリアルタイムに値の変更が適用されるようにする。

ComponentBoxの呼び出し時にpollIntervalを与えるように修正。

```ruby:app/views/comments/index.html.erb
<%= react_component('CommentBox', url: '/api/v1/comments.json', pollInterval: 2000) %>
```

WebAPIにアクセスする処理を`loadCommentsFromServer()`というメソッドに切り出す。
`componentDidMount()`メソッドで渡されたpollInterval間隔で`loadCommentsFromServer()`メソッドを呼び出すようにする。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      success: function(result) {
        this.setState({data: result.data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
});
// ...省略
```

`http://localhost:3000`にアクセスして、開発者ツールでネットワークキャプチャしてみると、2秒毎にアクセスしているのが見られる。

### サーバを実装する

ここはtutorialには無い内容。

現在のところ、実装したWebAPIは固定のJSONを返却している。
データベースにアクセスしてJSONを返却できるように実装する。

#### modelの作成

```bash
$ rails g model comment author:string text:text
$ rake db:migrate
```

#### controllerの実装

##### get
```ruby:app/controller/api/v1/comments_controller.rb
class Api::V1::CommentsController < ApplicationController
  def index
    @data = Comment.all
  end
end
```

データベースからアクセスしてjsonを返却するようにした。

まだデータがないので、localhost:3000にアクセスしても1件もCommentは表示されない。

rails consoleよりデータを作成してみる。

```bash
$ rails c
irb(main):001:0> Comment.create!({author: 'Pete Hunt',text: 'This is one comment'})
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "comments" ("author", "text", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["author", "Pete Hunt"], ["text", "This is one comment"], ["created_at", "2015-04-29 07:12:53.220217"], ["updated_at", "2015-04-29 07:12:53.220217"]]
   (0.7ms)  commit transaction
=> #<Comment id: 1, author: "Pete Hunt", text: "This is one comment", created_at: "2015-04-29 07:12:53", updated_at: "2015-04-29 07:12:53">
```

再度localhost:3000にアクセスすると、作成したCommentが表示される。

##### post

次にCommentを作成できるAPIを実装する。

tutorialのサーバは、getしようとpostしようとcomment全件を配列で返す！というちょっと乱暴な感じで実装されている。

それはどうかなー、という感じがするので、POSTした時は作成したComment 1件を返す、という実装にする。


まずは返却するjsonを定義する。

```ruby:app/views/api/v1/comments/show.json.jbuilder
json.extract!(@comment, :author, :text)
```

次にcreateアクションを実装する。

```ruby:app/controller/api/v1/comments_controller.rb
class Api::V1::CommentsController < ApplicationController
  # ...省略

  def create
    @comment = Comment.create(comment_params)
    render :show, status: :created
  end

  private

  def comment_params
    params.permit(:author, :text)
  end
end
```

試しにcurlでPOSTを投げてCommentを作成したい。そのためにもう一箇所変更する。

RailsではCSRFの対策が入っていて、CSRF tokenが一致しない場合には例外を投げるという動作をする。

すげー便利！なんだけど今回はセッションの認証とかないのでこの動作を、一致しない場合はセッションを空にする、に変更する。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end
```

デフォルトでは`protect_from_forgery with: :exception`になっているはず。これを`:null_session`に変える。

curlでPOSTを投げる。

```bash
$ curl -X POST http://localhost:3000/api/v1/comments.json -H 'content-type: application/json' -d '{"author": "Jordan Walke", "text": "This is *another* comment"}'
```

`http://localhost:3000`にアクセスして、Commentが作成されてればok。

### [Adding new comments](https://facebook.github.io/react/docs/tutorial.html#adding-new-comments)

CommentFormにCommentを入力できるFormを実装する。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
// ...省略
var CommentForm = React.createClass({
  render: function() {
    return (
      <form className="commentForm">
        <input type="text" placeholder="Your name" />
        <input type="text" placeholder="Say something..." />
        <input type="submit" value="Post" />
      </form>
    );
  }
});
// ...省略
```

次にFormをsubmitした時の動作を実装する。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
// ...省略
var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});
// ...省略
```

submitイベントが発行された時に、`handleSubmit()`を実行するようにした。

実装したのはsubmitされた時にauthorとtextの入力値にアクセスできるようにしたのと、イベント終了時にinputを空にするところ。

まだサーバへPOSTはしていない。ここで重要なのはinputの属性で`ref="author"`と指定しているところ。

こうしておくと、ReactのComponentの中で`this.refs.hoge`で要素にアクセスできるようになる。

次にサーバへPOSTを投げてCommentを作成する処理を実装したい、がその前にその処理がどこで実行されるべきか、ということについて考える。

今回実装しているComponentの構成は以下になる。

```
CommentBox
├── CommentList
│   └── Comment
└── CommentForm
```

Commentが作成された時に実際に増えるのは、CommentListの管理しているCommentで、それはCommentBoxのstateが渡されている。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
// ...省略
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} /> // ここ！
        <CommentForm />
      </div>
    );
  }
});
// ...省略
```

つまりデータの大元はCommentBoxで保持しているstateになる。

Reactでは、データの変更ができるのはstateのみで、propsは決して直接いじってはいけない。(これ重要！)

今回はPOSTした後にはすぐに画面をリフレッシュしたいので、stateをいじる必要がある。

ということで、stateを持っているCommentBoxに処理を実装するのが正しい。

まずはCommentFormのsubmitイベントが実行された時に、CommentBoxに実装されたメソッドが呼ばれるようにする。

CommentBoxからCommentFormにcallbackを渡すことでこれを実現する。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  // ...省略
  handleCommentSubmit: function(comment) {
    // TODO: submit to the server and refresh the list
  },
  // ...省略
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} /> //ここでcallback渡す！
      </div>
    );
  }
});

// ...省略

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    this.props.onCommentSubmit({author: author, text: text}); // ここでcallback実行する！
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  // ...省略
});

```

これでcallbackは渡せた。試しに`handleCommentSubmit()`メソッドでconsole.logしてみれば、ちゃんとメソッドが呼び出されていることが確認できるはず。

`handleCommentSubmit()`にajaxでPOSTを投げる処理を実装する。

```javascript
var CommentBox = React.createClass({
  // ...省略
  handleCommentSubmit: function(comment) {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: comment,
      success: function(data) {
        this.setState({data: this.state.data.concat([data])});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  // ...省略
});
```

POSTのレスポンスのbodyには作成したCommentが返却されるように実装したので、

```javascript
this.setState({data: this.state.data.concat([data])});
```
で返却されたCommentをstateに追加するように実装している。

ここでArray#concatを使っているのは、変更前に保持していたstateを直接変更したくないため。

下記の記事が非常に分かりやすい。

[React.jsのState](http://qiita.com/koba04/items/63267bcc918d76ac8767)

### [Optimization: optimistic updates](https://facebook.github.io/react/docs/tutorial.html#optimization-optimistic-updates)

ここはsubmitが実行された時にサーバ通信する前にstateを変更することで、ユーザへのフィードバックを早めよう！という内容。

今回はPOST時のレスポンスをtutorialと変えているのでそのまま適用できないけど、やるとしたらこんな感じ。

```javascript:app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
  // ...省略
  handleCommentSubmit: function(comment) {
    this.setState({data: this.state.data.concat([comment])});
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: comment,
      success: function(data) {
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  // ...省略
});
```

successの処理を頭に持ってきただけ。
でもこれって短い間(最大2秒)とはいえ、失敗した時には間違った結果がrenderされていることになる。どうなんだろう。

## [Congrats!](https://facebook.github.io/react/docs/tutorial.html#congrats)

おわりです。お疲れ様でした！