【rails/非同期】いいね機能をReact.jsで実装してみた。その①

【2016/12/17 追記】
React（flux）を使ったブログエンジンを開発しました！
何か参考になれば嬉しいです！

Notee
https://github.com/funaota/notee

__________________________

最近なにかと流行のreact先生に今回思い切ってチャレンジしてみる事にしました。（遅め笑）

### react触った印象
 - VirtualDOMにはめっちゃ感動しました。
 - id指定して、innerTextをどうたらこうたらってやらなくていいこと
 - State変更するだけでViewも変更されるんは熱い！
 - 思ってたよりも簡単！

### ちょっとなと思ったところ

 - jsxの書き方嫌いな人は嫌いそう笑
 - jsx内で条件分岐のcodeとか書きにくい（component化したほうがいいとかなんとか、まだよくわからん）
 - jsx内でインスタンス変数とかアクセス出来ない（？）

# 今回やったこと

今回やったことは、タイトル通り、**『いいね機能の実装』**です。
Facebookのいいね機能をイメージしてもらえれば大丈夫だと思います。

具体的なLikeモデルのSchemaは以下になっています。

```rb:Like
# == Schema Information
#
# Table name: likes
#
#  id         :integer          not null, primary key
#  user_id    :integer          not null
#  post_id    :integer          not null
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

```

具体的なイベントの流れは

 - ①いいねボタンを押す
 - ②LikeモデルにajaxでPOSTする
 - ③いいねボタンのViewを更新する
 - ④いいね回数のViewを更新する
 
上記の流れでDELETE versionも実装しました。

さて早速実装内容について説明します。

## ①reactを使える環境にする

詳しくはここを見てください
https://github.com/reactjs/react-rails#reactjs-builds

```rb:Gemfile
gem 'react-rails', '~> 1.7.0'
```

`$ bundle install`
`$ rails g react:install`

```rb:config/environments/development.rb
Rails.application.configure do

  ...

  config.react.variant = :development

  ...

end
```

## ②Componentをgenerateします

`rails g react:component Like`

このコマンドをうつと、`app/assets/javascripts/components`に`like.js.jsx`が生成されます。
このファイルに実際のコードを書いて行きます。

## ③とりあえず、Hello World!を表示させるためのjsを完成させ、react_componentで表示させる


Hello World!を表示させるためのjsはこんな感じ

```js:like.js.jsx

Like = React.createClass({
  render: function() {
    return(
      <div>Hello World!</div>
    );
  }
});

```

View側はこう書く

```rb:posts.html.slim
- @posts.each do |post|
  p = post.title
  = react_component 'Like'
```

これで、HelloWorldが、各Postに表示されるはずです。
ここのHelloWorldをいいねボタンに変えて、処理をすれば完成です！
じゃあ、いよいよ本題に入ります。

## ④いいねButtonのView作成と、対応するメソッドを用意します。

### Viewの作成
`<button onClick={this.ajaxMain}>いいね!</button>`
Buttonタグで作成し、onClickでjs内のメソッドを指定する

### 対応するメソッドの作成
```js
ajaxMain: function(){
  console.log("hello world!");
}
```
とりあえず、hello worldとコンソールに表示させるコードにします。
以下が全体のコードになります。

```js:like.js.jsx

Like = React.createClass({

  ajaxMain: function(){
    console.log("hello world!");
  }

  render: function(){
    return(
      <button onClick={this.ajaxMain}>いいね!</button>
    );
  }
});

```

これで、いいねボタンのViewが表示され、ボタンを押すと、
consoleに"hello world"に表示されるはずです。

## ⑤使用するStateを定義する

今回は以下のStateを持つ事にします。

 - いいねの合計数
 - いいねされているかどうかの状態

```js:like.js.jsx

Like = React.createClass({

  getInitialState: function(){
      return {
          counts: 0,         //いいねの合計数
          is_liked: false    //いいねされているかどうかの状態
      };
  },

  ajaxMain: function(){
      console.log("hello world!");
  }

  render: function(){
      return(
        <button onClick={this.ajaxMain}>いいね!</button>
      );
  }
});

```

## ⑤LikeをCreateするメソッドを用意します。

### Controllerの流れ

 - ①ajaxでparams[:post_id]を受け取る
 - ②Likeを作成する
 - ③jsonで、status（ajaxが成功したかどうか）、like（あとで使う笑）、counts(いいねの合計数)、liked（いいねされてるかどうか）を返す

```rb:likes_controller.rb
  def create
    @like = Like.new(user_id: current_user.id, post_id: params[:post_id])

    respond_to do |format|
      if @like.save
        format.html { redirect_to :back, notice: 'Like was successfully created.' }
        format.json { render json: {status: 'success', like: @like, counts: Like.where(post_id: @like.post_id).count, liked: true} }
      else
        format.html { render :new }
        format.json { render json: @like.errors, status: :unprocessable_entity }
      end
    end
  end
```

## ⑥ajaxでparams[:post_id]を送り、resuponseを受け取るコードを記述する

```js:like.js.jsx

Like = React.createClass({
    ajaxMain: function(method){
        $.ajax({
            url: this.props.url,                               //このURLは次でセットします
            type:method,                                       //今回はPOSTで送ります
            dataType: 'json',
            cache: false,
            data: {post_id: this.props.post},                  //このpostも次でセットします
            success: function(data) {
                if(data.like){                                 //ここはあとで活きてきます笑
                    this.setState({is_liked: data.liked});     //responseのlikedをstateのis_likedに反映させます
                };
                this.setState({counts: data.counts});          //responseのcountsをstateのcountsにに反映させます
            }.bind(this),                                      //bindしないとthisが使えなくてエラりますw
            error: function(xhr, status, err) {
                console.error(this.props.url, status, err.toString());
            }.bind(this)
        });
    },

    render: function(){
        return(
            <button onClick={this.ajaxMain.bind(this, "POST")}>いいね!</button>
        );
    }
});

```

## ⑦view側で、propsにパラメーターを渡します。

`api_likes_url`は、routeで指定したajaxのapiを指定します。環境に会わせて随時変更してください。

```rb:posts.html.slim
- @posts.each do |post|
  p = post.title
  = react_component 'Like', post: post.id, url: api_likes_url
```

### これで、ひとまずLikeを非同期で作成するまで完成しました！

具体的なイベントの流れ

 - ~~①いいねボタンを押す~~
 - ~~②LikeモデルにajaxでPOSTする~~
 - ③いいねボタンのViewを更新する
 - ④いいね回数のViewを更新する

次は③から活きたいと思います。

今日もまた蛍の光がお店でなりだしたので、続きはまた明日！！
Reactの説明難しい…


### その②もかきますた！

http://qiita.com/funao/items/979be14210abbbc9b493#_reference-0bfc15f8e74dae27325c