Rails4系プロジェクトにReact + webpacker（css-modules）を導入する

既存のRailsプロジェクトにReactとwebpackerを導入した時の手順をメモします。
（まだ本番運用はしてません）

環境
===
- ruby 2.4.2
- Rails 4.2.8
- Mac OS 10.12.6
- webpacker 3.0.2

必要なgemをインストール
===

Gemfile更新してbundle install

```ruby:Gemfile
gem 'react-rails'
gem 'webpacker', '~> 3.0'
```

yarnが必要なのでインストールしておく。

```zsh
$ brew install yarn
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core, homebrew/php).

...中略

Error: Xcode alone is not sufficient on Sierra.
Install the Command Line Tools:
  xcode-select --install
```

※xcodeまわりで怒られたら言われるがままにインストール。

```zsh
$ xcode-select --install
xcode-select: note: install requested for command line developer tools
```

webpackerをインストール
===

railsのバージョンによってコマンドが異なるので注意。

```zsh
bundle
bundle exec rails webpacker:install

# OR (on rails version < 5.0)
bundle exec rake webpacker:install
```

https://github.com/rails/webpacker#installation


```zsh
$ bundle exec rake webpacker:install
      create  config/webpacker.yml
Copying webpack core config and loaders
      create  config/webpack
      create  config/webpack/development.js
      create  config/webpack/environment.js
      create  config/webpack/production.js

...中略

├─ webpack-dev-middleware@1.12.2
├─ webpack-dev-server@2.9.5
├─ websocket-driver@0.7.0
├─ websocket-extensions@0.1.3
├─ yargs-parser@4.2.1
└─ yargs@6.6.0
warning No license field
✨  Done in 5.97s.
Webpacker successfully installed 🎉 🍰
```

さらにreact用にテンプレートをインストール。

```zsh
$ bundle exec rake webpacker:install:react
Webpacker is installed 🎉 🍰

...中略

├─ ua-parser-js@0.7.17
└─ whatwg-fetch@2.0.3
warning No license field
✨  Done in 5.82s.
Webpacker now supports react.js 🎉
```

インストール成功 :clap: 


Reactをインストール
===

例のごとくコマンド叩く。

```zsh
$ rails generate react:install
Expected string default value for '--helper'; got false (boolean)
Expected string default value for '--assets'; got false (boolean)
Expected string default value for '--decorator'; got true (boolean)
Expected string default value for '--decorator'; got true (boolean)
Expected string default value for '--jbuilder'; got true (boolean)
      create  app/javascript/components
      create  app/javascript/components/.gitkeep
warning package.json: No license field
warning No license field
warning "@rails/webpacker > postcss-cssnext@3.0.2" has unmet peer dependency "caniuse-lite@^1.0.30000697".
warning " > webpack-dev-server@2.9.7" has unmet peer dependency "webpack@^2.2.0 || ^3.0.0".
warning "webpack-dev-server > webpack-dev-middleware@1.12.2" has unmet peer dependency "webpack@^1.0.0 || ^2.0.0 || ^3.0.0".
warning No license field
      append  app/javascript/packs/application.js
      create  app/javascript/packs/server_rendering.js
```

サンプルつくってみる。

```zsh
$ rails g react:component HelloWorld greeting:string
      create  app/javascript/components/HelloWorld.js
```

以下のようなjsファイルが生成されるので、

```js:app/javascript/components/HelloWorld.js
import React from "react"
import PropTypes from "prop-types"
class HelloWorld extends React.Component {
  render () {
    return (
      <div>
        <div>Greeting: {this.props.greeting}</div>
      </div>
    );
  }
}

HelloWorld.propTypes = {
  greeting: PropTypes.string
};
export default HelloWorld
```

viewから呼び出してみる。

まずはlayoutsでwebpackのファイルを読み込むようにして、

```ruby:app/views/layouts/application.html.slim
...
= javascript_pack_tag 'application'
...
```

ヘルパで呼び出す。

```ruby:app/views/hoge/index.html.slim
= react_component('HelloWorld', {greeting: 'Hello'})
```

`/hoge/index`にアクセスすると、「Greeting: Hello」と表示された :sunglasses: 

css-modulesを使う
===
webpack使ってるし、せっかくなのでcss-modules使ってみる。

スタイル作成してimportする。

```scss:app/javascript/components/HelloWorld.scss
.hoge {
  color: red;
}
```

```diff:app/javascript/components/HelloWorld.js
import React from "react"
import PropTypes from "prop-types"
+ import Style from './HelloWorld.scss'
class HelloWorld extends React.Component {
  render () {
    return (
      <div>
-         <div>Greeting: {this.props.greeting}</div>
+         <div className={Style.hoge}>Greeting: {this.props.greeting}</div>
      </div>
    );
  }
}

HelloWorld.propTypes = {
  greeting: PropTypes.string
};
export default HelloWorld
```

layoutsにも追記忘れずに。

```ruby:app/views/layouts/application.html.slim
...
= stylesheet_pack_tag 'application'
...
```

css-modules使うためにloaderの設定方法を変更する。（css-loaderで`modules: true`にする。）

設定はdocsのとおりにした。
https://github.com/rails/webpacker/blob/master/docs/webpack.md#overriding-loader-options-in-webpack-3-for-css-modules-etc
※バージョンによって変わってくるので注意。以下はwebpacker 3.0.2の場合。

```diff
const { environment } = require('@rails/webpacker')
+ const merge = require('webpack-merge')
+ 
+ const myCssLoaderOptions = {
+   modules: true,
+   sourceMap: true,
+   localIdentName: '[name]__[local]___[hash:base64:5]'
+ }
+ 
+ const CSSLoader = environment.loaders.get('style').use.find(el => el.loader === 'css-loader')
+ 
+ CSSLoader.options = merge(CSSLoader.options, myCssLoaderOptions)

module.exports = environment
```

`webpack-merge`が必要なのでnpmでインストールする。

```zsh
$ npm install webpack-merge
```

`bin/webpack`コマンドでビルドする。

```zsh
$ bin/webpack
Hash: d9e1c05fb0115c67db9a
Version: webpack 3.10.0
Time: 4415ms
                                                Asset       Size  Chunks                    Chunk Names
             server_rendering-2fc6d35ea1f628531946.js    1.96 MB       0  [emitted]  [big]  server_rendering
                  application-e4edca0d11e9022b85f8.js    1.96 MB       1  [emitted]  [big]  application
                  hello_react-2fa08b964a0fbb7ea3bf.js    1.68 MB       2  [emitted]  [big]  hello_react
     application-58ab649efbf1ea4787a749efd9c58636.css  419 bytes       1  [emitted]         application
server_rendering-58ab649efbf1ea4787a749efd9c58636.css  419 bytes       0  [emitted]         server_rendering
                                        manifest.json  380 bytes          [emitted]
  [28] ./app/javascript/components ^\.\/.*$ 236 bytes {0} {1} [built]
  [41] ./app/javascript/packs/application.js 730 bytes {1} [built]
  [42] ./app/javascript/packs/hello_react.jsx 739 bytes {2} [built]
  [43] ./app/javascript/packs/server_rendering.js 299 bytes {0} [built]
    + 44 hidden modules
Child extract-text-webpack-plugin node_modules/extract-text-webpack-plugin/dist node_modules/css-loader/index.js??ref--5-2!node_modules/postcss-loader/lib/index.js??ref--5-3!node_modules/resolve-url-loader/index.js!node_modules/sass-loader/lib/loader.js??ref--5-5!app/javascript/components/HelloWorld.scss:
       2 modules
```

ビルドできた :clap: 

`/hoge/index`にアクセスすると、「Greeting: Hello」が赤文字で表示された :sunglasses: 

Macのローカルでの作業はいったんここまで。
次はCIでの設定とかまとめていこうかな :bulb: 

参考
---
- [react-railsを使ってReactのTutorialをやってみる - Qiita](https://qiita.com/joe-re/items/96f12dda4a62470d1d7c)
- [Rails5.1から追加されると噂のWebpackerを使ってReact.jsを動かす。 - Qiita](https://qiita.com/yoshiokaCB/items/564ed0440f0428c0009a)
- https://github.com/reactjs/react-rails
- https://github.com/rails/webpacker