Rails エンジニア向け はじめての React.js 勉強会

# This is ...

- [Railsエンジニア向けはじめてのReact.js勉強会](http://eventdots.jp/event/575893) のための資料
- [参考プロジェクト](https://github.com/startup-technology/hajimete-no-react/tree/sample)
    - https://hajimete-no-react.herokuapp.com/

# はじめての React.js
## Virtual DOM と JSX
- ReactはjQueryのようにHTMLからDOMを組み立てません。
- 最初からJavaScriptのオブジェクトでDOMの構造を組み立て管理しています。
- JavaScriptで組み立てられた DOM（Virtual DOM）だと人間が読むのにツライので、JSXというHTMLをJavaScriptオブジェクトにコンパイルする仕組みが用意されています。

```js:jQuery
$('#id').append('<div>Seconds Elapsed' + this.state.secondsElapsed + '</div>');
```
```js:JSで書いたReact
// Objectで仮想DOMを作る。
return React.createElement(
  "div", null, "Seconds Elapsed: ", this.state.secondsElapsed
);
```

```jsx:JSXで書いたReact
// JSX
return (
  <div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
```

- JSXの中に { } でHTMLの中に動的なデータやJavacriptを挿入することができます。（ERBの<% %>、PHPの<?php ?>とだいたい同じです。JavaScriptでERB書いてる感じです）

## コンポーネント

- 幾つかの HTML で組み立てられた作られたパーツです。
- Bootstrap の Compornents のようなものです。
<img width="865" alt="スクリーンショット 2015-11-12 19.38.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/6389/fec613ef-13f9-2b4f-91c9-561fc3924507.png">
- Reactはコンポーネントごとに`class`を作ります。`class`の中にロジックとテンプレートを分離せずに同居した構成になっています。

### 実例

<img width="718" alt="HajimeteNoReact.png" src="https://qiita-image-store.s3.amazonaws.com/0/6389/4d8508ab-1947-dbb8-1fc3-5e6a25b316f6.png">

- コンポーネントは入れ子にできます。コンポーネントを上手に分けることができればとてもすっきりして見やすい。
- 同じ階層のコンポーネント間でデータをやり取りするときは親コンポーネントを作って中継します。

### コンポーネントの要素
- React のコンポーネントの要素について軽く触れます。

#### render()
- コンポーネントに必ず定義しないといけない関数。
- 仮想DOMをreturnします。

#### props
- コンポーネントに外部から与えられたデータ
- コンポーネントの中で変更してはいけない

```jsx
{/* HogeComponent 内で `this.props.foo` => "bar" という感じに取り出せる */}
<div>
  <HogeComponent foo="bar" />
</div>
```

#### state
- コンポーネント内部で管理するデータ
- コンポーネントの外から変更してはいけない
- 変更するときは `setState()` メソッドを使う。
    - `setState()` で state を変更すると、変更の差分が再描画される。

#### key
- 同じコンポーネントをリストで繰り返し配置する場合、Reactが要素の追加・削除・移動などを検出するために、ユニークになるデータを与えてあげる必要があります。
- key は設定しなくても動作はしますが、key を設定してあげることでReactがDOMの再描画を効率良く行ってくれるようになるのでパフォーマンスが上がります。

## RailsでReactするための設定

- [react-rails](https://github.com/reactjs/react-rails) というGemを使います。

```ruby:Gemfile
gem 'react-rails'
```

- generator を使うといい感じに環境ができる。

```bash:sh
rails g react:install
```

- コンポーネントも generator で作れる。

```bash:sh
rails g react:component [コンポーネント]
# => app/assets/javascripts/components/[コンポーネント].js.jsx ができあがる
```

- ヘルパーでコンポーネントをビューに設置する

```erb:erb
<%= react_component('[コンポーネント]') %>
```

- JSXはES6シンタックスが使えます。アロー関数 `() => {}` が便利です。
- 拡張子を `.js.jsx.coffee` にすると CoffeeScript でJSXを書くこともできます。

## JSX の注意点
- JavaScriptの予約語とかぶってるアトリビュートがリネームされている
    - class => className
    - for => htmlFor
- 閉じタグがない場合は `/>` でタグを閉じる。XHTMLっぽい。
    - `<br />`
- jQueryなどで外部からReactでレンダリングしたDOMをいじるとうまく動かない時があります。
    - 使う箇所の住み分けができていれば jQuery と一緒に使っても大丈夫。Bootstrapも使えます。
- エラーログが丁寧で解りやすいので、デベロッパーツールのコンソールみましょう。

---

# React.js ハンズオン

## 環境
- rails: 4.2.4
- react-rails: 1.5.0
- github: [startup-technology/hajimete-no-react](https://github.com/startup-technology/hajimete-no-react)

## 準備

```bash
$ git clone git@github.com:startup-technology/hajimete-no-react.git
$ cd hajimete-no-react
$ bundle install
```

- react-rails を追加

```rb:Gemfile
gem 'react-rails', '~> 1.5.0'
```
```bash
$ bundle install
```

- react の環境設定

```bash
$ rails g react:install
```
```
      create  app/assets/javascripts/components
      create  app/assets/javascripts/components/.gitkeep
      insert  app/assets/javascripts/application.js
      insert  app/assets/javascripts/application.js
      insert  app/assets/javascripts/application.js
      create  app/assets/javascripts/components.js
```

## コンポーネントを作る

```bash
$ rails g react:component Hello
```
```
      create  app/assets/javascripts/components/hello.js.jsx
```

- このようなファイルができている

```jsx:app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({

  render: function() {
    return <div />;
  }
});
```

### コントローラーとビューを作成

```bash
$ rails g controller hello
```

- HelloController に index アクションを追加

```rb:app/controllers/hello_controller.rb
class HelloController < ApplicationController
  def index
  end
end
```

- routes を設定

```rb:config/routes.rb
root 'hello#index'
```

- ビューにReactコンポーネントを追加します

```erb:app/views/hello/index.html.erb
<%= react_component 'Hello' %>
```

## 確認してみる

- コンポーネントを修正します

```jsx:app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({

  render: function() {
    return (
      <div>
        <h1>Hello React!</h1>
      </div>
    );
  }
});
```

- railsを起動して確認してみましょう

```bash
$ rails s
```

http://localhost:3000/

<img width="374" alt="スクリーンショット 2015-12-01 12.59.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/6389/66965b8b-840f-f3aa-7e3b-6a726cf9b798.png">

## リストを作ってみる

- HelloList というコンポーネントを作って並べてみます。

```bash
$ rails g react:component HelloList
```

- HelloList は、親コンポーネントから `hello` という `props` を

```jsx:app/assets/javascripts/components/hello_list.js.jsx
var HelloList = React.createClass({

  render: function() {
    return (
      <li>Hello {this.props.hello}</li>
    );
  }
});
```

- Hello コンポーネントも少し直します。
- Hello コンポーネントの中に HelloList コンポーネントを設置してみます。

```jsx:app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({

  render: function() {
    return (
      <div>
        <h1>Hello React!</h1>
        <ul>
          <HelloList hello="hoge" />
          <HelloList hello="fuga" />
        </ul>
      </div>
    );
  }
});
```

- 確認してみましょう

<img width="290" alt="スクリーンショット 2015-12-01 13.54.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/6389/7e4d2f10-2b2e-ec7c-d375-568c0cbfa4b2.png">

## リストを動的に作ってみる

- Hello コンポーネントをさらに直します
- `getInitialState` で、`state` の初期値を設定します。
- `map` を使い、配列でエレメントを作成しています。
- エレメントを一旦変数に格納してから埋め込むことができます。

```jsx:app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({
  getInitialState: function() {
    return {
      hello: ['hoge', 'fuga']
    }
  },

  render: function() {
    var helloList = this.state.hello.map(hello => {
      return <HelloList hello={hello} />;
    });

    return (
      <div>
        <h1>Hello React!</h1>
        <ul>
          {helloList}
        </ul>
      </div>
    );
  }
});
```

- 先ほどと同じ内容が表示されていると思います。
<img width="290" alt="スクリーンショット 2015-12-01 13.54.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/6389/7e4d2f10-2b2e-ec7c-d375-568c0cbfa4b2.png">

- この時、コンソールを見ると Warning が発生しています。

<img width="700" alt="スクリーンショット 2015-12-01 14.02.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/6389/d7f5ef11-9d73-5542-fe9d-4b35f489fc09.png">

- Reactに配列で要素を設置する場合はユニークな`key`を設定してあげる必要があります。
- HelloListにユニークな`key`を渡すように修正してみましょう。

```jsx:app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({
  getInitialState: function() {
    return {
      hello: [
        {id: 1, text: 'hoge'},
        {id: 2, text: 'fuga'}
      ]
    }
  },

  render: function() {
    var helloList = this.state.hello.map(hello => {
      return <HelloList key={hello.id} hello={hello.text} />;
    });

    return (
      <div>
        <h1>Hello React!</h1>
        <ul>
          {helloList}
        </ul>
      </div>
    );
  }
});
```

- これでワーニングが消えたはずです。

## フォームを作ってみる

- HelloForm コンポーネントを作ります。

```bash
$ rails g react:component HelloForm
```

- JSXでもHTMLと同様に`onChange`などのイベントハンドラを設定することができます。
    - イベントハンドラの設定は `onChange={this.foge}` のように関数自体を渡していることに注意してください。
    - HTML と同じ感覚で `onChange={this.foge()}` のように書いてしまうと、render の時に関数が実行されてしまいます。（ハンドラの中で `setState` していると無限ループになります）

```jsx:app/assets/javascripts/components/hello_form.js.jsx
var HelloForm = React.createClass({
  getInitialState: function() {
    return {
      value: ''
    }
  },

  handleChange: function(e) {
    this.props.onChangeForm(e.target.value);
  },

  render: function() {
    return (
      <input type="text" onChange={this.handleChange} />
    );
  }
});
```

- props に関数を渡すと、子コンポーネントから親コンポーネントに引数でデータを渡すことができるようになります。

```jsx:app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({
  getInitialState: function() {
    return {
      hello: [
        {id: 1, text: 'hoge'},
        {id: 2, text: 'fuga'}
      ],
      value: 'React!'
    }
  },

  handleChangeForm: function(value) {
    this.setState({ value: value });
  },

  render: function() {
    var helloList = this.state.hello.map(hello => {
      return <HelloList key={hello.id} hello={hello.text} />;
    });

    return (
      <div>
        <h1>Hello {this.state.value}!</h1>
        <HelloForm onChangeForm={this.handleChangeForm} />
        <ul>
          {helloList}
        </ul>
      </div>
    );
  }
});
```

- このようになります。

![ae1353e9b9da3a0253623099ce801b1f.gif](https://qiita-image-store.s3.amazonaws.com/0/6389/f228e32e-2264-da81-cdfc-d60bd9309087.gif)

# まとめ

- オライリーから本が出ています。[入門 React](http://www.amazon.co.jp/dp/4873117194/)
- Qiitaの記事だと [一人React.js Advent Calendar 2014](http://qiita.com/advent-calendar/2014/reactjs) 、[react-railsを使ってReactのTutorialをやってみる](http://qiita.com/joe-re/items/96f12dda4a62470d1d7c) などが分かりやすいと思います。