React 触ってみたよ！

# React 触ってみた
* 実は Qiita で記事書くの初めてです！どうぞお手柔らかに！
* 世の流れにとり残されてる感があったので、重い腰をあげて React に触れてみたのでその備忘録。
* できるだけモダンなフロントエンド開発をするための、素地を整えるところまでをゴールとする。

# 構成
* Rails 4.2.x
* React
* ES6
* Babel
* Webpack
* gulp
* [Material Design Lite](https://getmdl.io/)

# 方針
* 開発環境は vagrant(ubuntu) でやる
* 既存の Rails アプリケーションに乗せる
* フロントエンドの管理に関するものは、すべて npm で管理する(gem ではやらない)
* フロントエンドに関するコードはすべて `app_root/frontend` に置く
* 自分で書いた js, jsx 部
  * `app_root/app/assets/javascripts/dist/main.js` に出力する
* 外部ライブラリ js 部
  * `app_root/app/assets/javascripts/dist/vendor.js` に出力する
* 自分で書いた css 部
  * `app_root/app/assets/stylesheet/dist/main.css` に出力する
* 外部ライブラリ css 部
  * `app_root/app/assets/stylesheet/dist/vendor.css` に出力する

# まず Rails から
#### 軽くセットアップ
```zsh:command
$ cd ~/
$ rails new react-test -d mysql
$ cd react-test
$ bin/rake db:create db:migrate
```
#### 検証に必要な分だけの、最低限のコード書く
```rb:Gemfile.rb
gem 'slim-rails'
```
```zsh:command
$ bin/rails g controller test
```
```rb:app/controllers/test_controller.rb
class TestController < ApplicationController
  def index
  end
end
```
```erb:app/views/test/index.html.slim
#container
```
```rb:config/routes.rb
root 'test#index'
```
```zsh:command
$ bin/rails s -b 0.0.0.0
```

これで準備完了。

# React 導入準備
#### node いれる
```zsh:command
$ curl -L git.io/nodebrew | perl - setup
# .bashrc, .zshrc などに PATH 書く
export PATH=$HOME/.nodebrew/current/bin:$PATH
$ source ~/.zshrc
$ nodebrew ls-remote
# 20160603 時点での Stable は v5.11.1 だった(https://nodejs.org/en/blog/)
$ nodebrew install-binary v5.11.1
$ nodebrew use v5.11.1
$ node -v
v5.11.1
$ npm -v
3.8.6
```

#### app_root/frontend 以下の準備
```zsh:command
$ cd ~/react-test
$ mkdir frontend
$ cd frontend
$ npm init
# とりあえず全部エンター押しとく
```
これで `package.json` がとりまできた。

# パッケージ準備

#### React + ES6 + Babel + Webpack + gulp + Material Design Lite を使えるようにする
```zsh:command
# React 用
$ npm install --save react react-dom react-router react-tap-event-plugin
# ES6, Babel, Webpack 用
$ npm install --save-dev babel babel-loader babel-core babel-preset-react babel-preset-es2015 webpack
# gulp 用
$ npm install --save-dev gulp gulp-autoprefixer gulp-concat gulp-concat-css gulp-ruby-sass gulp-webpack
# Material Design Lite 用
$ gulp install --save material-design-lite
```
`--save` は「パッケージ（≒プロジェクト）の実行に必要なパッケージの定義」らしい
`--save-dev` は「パッケージの開発に必要なパッケージの定義」らしい

```json:package.json
{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel": "^6.5.2",
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.0",
    "gulp-concat": "^2.6.0",
    "gulp-concat-css": "^2.3.0",
    "gulp-ruby-sass": "^2.0.6",
    "gulp-webpack": "^1.5.0",
    "webpack": "^1.13.1"
  },
  "dependencies": {
    "material-design-lite": "^1.1.3",
    "react": "^15.1.0",
    "react-dom": "^15.1.0",
    "react-router": "^2.4.1",
    "react-tap-event-plugin": "^1.0.0"
  }
}
```
ここまでで、`package.json` はこうなった。

# ビルド周り
```json:package.json
"scripts": {
  "build": "gulp build"
},
```
`package.json` を上記のように編集する。
これで、`$ npm run build` で、 `$ gulp build` が走るようになる。

```zsh:command
$ cd ~/react-test/frontend
$ vim gulpfile.js
```
```js:gulpfile.js
var gulp = require('gulp');
var webpack = require('gulp-webpack');
var concat = require('gulp-concat');
var sass = require('gulp-ruby-sass');
var autoprefixer = require('gulp-autoprefixer');
var concatCss = require('gulp-concat-css');

const JS_SRC = '';
const JS_DEST = '../app/assets/javascripts/dist';
const CSS_SRC = 'src/stylesheets/main.scss';
const CSS_DEST = '../app/assets/stylesheets/dist';

var webpackConfig = {
  entry: {
    main: './src/javascripts/main.js',
  },
  output: {
    filename: 'main.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel?presets[]=react,presets[]=es2015'
      }
    ]
  }
}

gulp.task('js:main', function() {
  gulp.src(JS_SRC)
  .pipe(webpack(webpackConfig))
  .pipe(gulp.dest(JS_DEST));
});

gulp.task('js:vendor', function() {
  gulp.src([
    'node_modules/material-design-lite/material.min.js'
  ])
  .pipe(concat('vendor.js'))
  .pipe(gulp.dest(JS_DEST));
});

gulp.task('css:main', function(){
  sass(CSS_SRC, { style: 'expanded', bundleExec: true })
  .pipe(autoprefixer())
  .pipe(concatCss('main.css'))
  .pipe(gulp.dest(CSS_DEST));
});

gulp.task('css:vendor', function() {
  gulp.src([
    'node_modules/material-design-lite/material.min.css'
  ])
  .pipe(concatCss('vendor.css'))
  .pipe(gulp.dest(CSS_DEST));
});

gulp.task('build', ['js:main', 'js:vendor', 'css:main', 'css:vendor']);
```
これで、

* 自分で書いた js, jsx 部
  * `app_root/app/assets/javascripts/dist/main.js` に出力する
* 外部ライブラリ js 部
  * `app_root/app/assets/javascripts/dist/vendor.js` に出力する
* 自分で書いた css 部
  * `app_root/app/assets/stylesheet/dist/main.css` に出力する
* 外部ライブラリ css 部
  * `app_root/app/assets/stylesheet/dist/vendor.css` に出力する

という目的が達成できるようになった。

# ディレクトリ構成
これで開発準備は整った。
下記実現したいディレクトリ構成。

```zsh:app/frontend
└── app/frontend
    ├── gulpfile.js
    ├── package.json
    ├── src
    │   ├── javascripts
    │   │   ├── components
    │   │   │   └── test_page
    │   │   │       ├── fuga.js
    │   │   │       ├── hoge.js
    │   │   │       └── index.js
    │   │   ├── main.js
    │   │   └── utils
    │   └── stylesheets
    │       ├── main.scss
    │       └── pages
    │           └── test_page.scss
    └── test
```

# ここでもう一度 Rails 側の設定をする
```rb:config/initializers/assets.rb
Rails.application.config.assets.precompile += %w(dist/main.js)
Rails.application.config.assets.precompile += %w(dist/vendor.js)
Rails.application.config.assets.precompile += %w(dist/main.css)
Rails.application.config.assets.precompile += %w(dist/vendor.css)
```
これで、 `*/dist` 以下に出力されたファイルを、アセットパイプラインに乗せて、Rails 側から指定できるようになる。

```rb:app/views/layouts/application.html.slim
= javascript_include_tag 'dist/main'
= javascript_include_tag 'dist/vendor'
= stylesheet_link_tag 'dist/main'
= stylesheet_link_tag 'dist/vendor'
= stylesheet_link_tag 'https://fonts.googleapis.com/iconfamily=Material+Icons'
```
`<body>...</body>` の最後で読み込む必要がある点に注意。

# main.js について
```js:app/frontend/src/javascripts/main.js
import React from 'react';
import ReactDom from 'react-dom';
import { Router, Route, Link, browserHistory } from 'react-router';
import injectTapEventPlugin from "react-tap-event-plugin";

injectTapEventPlugin();

import TestPage from './components/test_page/index';

ReactDom.render((
  <Router history={browserHistory}>
    <Route path='/' component={TestPage} />
  </Router>
),document.querySelector('#container'));
```

* `main.js` は Webpack と gulp によって、`app_root/app/assets/javascripts/dist/main.js` にビルドされる。
* `react-router` により、マッチした path があると、`component={XXXXXX}` で指定したコンポーネントを表示してくれる。
* Rails 側で用意した `#container` に、React で書いた分をぼこっとマウントする感じ。

# React で表示してみる
```js:frontend/src/javascripts/components/test_page/index.js
import React from 'react';

export default class TestPage extends React.Component {
  constructor() {
    super();
    this.state = {
    };
  }

  render() {
    return (
      <div className='mdl-grid'>
        <div className='mdl-cell mdl-cell--2-offset mdl-cell--8-col'>
          <button className='mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect'>ALL IS WELL!</button>
        </div>
      </div>
    );
  }
}
```

* js の予約語との兼ね合いにより、`class` => `className`, `for` => `htmlfor` と書かなければならないなど、jsx はちょっと癖がある。

# 表示！
さあ、ここまできたら、ブラウザで確認してみよう！
マテリアルデザインっぽいボタンがポツリと出現しているはず！
これにてモダンなフロントエンド開発の素地作り終了！
<img width="672" alt="スクリーンショット 2016-06-04 0.58.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/65535/a9573346-4c3c-2e99-dcfc-e026d762c3fe.png">

# コード
https://github.com/mugiwaranonoma/react-test
一応 git にあげておいた。

# 今後
* gulpfile.js もっと書き込んでいかねば(gulp 触るのも初めてだった)
* redux 導入したい
* redux-router(?) で、SPA なのに URL がきちんと確認できるようにしたい
* ESLint いれたい
* Webpack と gulp が混在してるのどうにかしたい
* node_modules 以下のファイルを、Webpack もビルドできるのなら、Webpack だけにできそうな感じはある