RailsでReact.jsやってみる（react_on_rails編）


RailsでReact.jsと言えばreact-railsのgemが有名ですが、react_on_railsというものもあるので使ってみた、というかジェネレーターで作成されるサンプル(Hello World)を動かすまでやってみました。


# 環境
- OS: CentOS 6.7 64bit (VirtualBox)
- Ruby: 2.2.2
- node: 5.4.1
- npm: 3.3.12


## 1. まずはRailsプロジェクトの作成

```
[root@localhost rails]# mkdir react_on_rails_sample
[root@localhost rails]# cd react_on_rails_sample/
[root@localhost react_on_rails_sample]# bundle init
Writing new Gemfile to /var/rails/react_on_rails_sample/Gemfile
[root@localhost react_on_rails_sample]# vi Gemfile 
------------------------------
# A sample Gemfile
source "https://rubygems.org"

gem "rails"　←コメントアウトを外す
------------------------------

[root@localhost react_on_rails_sample]# bundle install --path=tmp/bundler

[root@localhost react_on_rails_sample]# bundle exec rails new .
       exist  
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  .gitignore
    conflict  Gemfile
Overwrite /var/rails/react_on_rails_sample/Gemfile? (enter "h" for help) [Ynaqdh] Y
       force  Gemfile
      create  app
      create  app/assets/javascripts/application.js
      ・・・
[root@localhost react_on_rails_sample]# vi Gemfile
---------------------
gem 'react_on_rails'　←末尾に追加
---------------------

[root@localhost react_on_rails_sample]# bundle install

```
gemをグローバルにインストールしたくないので、bundle installでpathオプションを指定しています。

## 2. react_on_railsのサンプルを試してみる
react_on_railsの[Getting Started](https://github.com/shakacode/react_on_rails#getting-started)をやっていきます。

```
[root@localhost react_on_rails_sample]# bundle exec rails generate react_on_rails:install --help
Running via Spring preloader in process 2638
Usage:
  rails generate react_on_rails:install [options]

Options:
  -R, [--redux], [--no-redux]                          # Install Redux gems and Redux version of Hello World Example. Default: false
  -S, [--server-rendering], [--no-server-rendering]    # Add necessary files and configurations for server-side rendering. Default: false
  -j, [--skip-js-linters], [--no-skip-js-linters]      # Skip installing JavaScript linting files. Default: false
  -L, [--ruby-linters], [--no-ruby-linters]            # Install ruby linting files, tasks, and configs. Default: false
  -H, [--heroku-deployment], [--no-heroku-deployment]  # Install files necessary for deploying to Heroku. Default: false
  -b, [--skip-bootstrap], [--no-skip-bootstrap]        # Skip integrating Bootstrap and don't initialize files and regarding configs. Default: false
      [--ignore-warnings], [--no-ignore-warnings]      # Skip warnings. Default: false
・・・

```

helpオプションで利用できるオプションを確認しました。
今回はサーバーサイドレンダリングやりたいのでSオプションを指定します。

```
[root@localhost react_on_rails_sample]# bundle exec rails generate react_on_rails:install -S
Running via Spring preloader in process 2727
fatal: Not a git repository (or any of the parent directories): .git
ERROR: You have uncommitted code. Please commit or stash your changes before continuing
ERROR: react_on_rails generator prerequisites not met!
```
エラーが出ました。。
どうやらgit管理していないといけないようなので、git init〜commitまでします。

```
[root@localhost react_on_rails_sample]# git init
Initialized empty Git repository in /var/rails/react_on_rails_sample/.git/
[root@localhost react_on_rails_sample]# git add .
[root@localhost react_on_rails_sample]# git commit -m "init"
[master (root-commit) 1d66a8b] init
 40 files changed, 1049 insertions(+), 0 deletions(-)
・・・
```

それでは、改めて実行。

```
[root@localhost react_on_rails_sample]# bundle exec rails generate react_on_rails:install -S
Running via Spring preloader in process 2776
ERROR: You have uncommitted code. Please commit or stash your changes before continuing
ERROR: react_on_rails generator prerequisites not met!

```

git commitしたにも関わらずエラーが出ます。。
調べてみたところ下記の箇所でgit commitしているかチェックしています。

```ruby:react_on_rails-2.3.0/lib/react_on_rails/git_utils.rb
module ReactOnRails
  module GitUtils
    def self.uncommitted_changes?(message_handler)
      return false if ENV["COVERAGE"]
      status = `git status`
      return false if status.include?("nothing to commit, working directory clean")
      error = "You have uncommitted code. Please commit or stash your changes before continuing"
      message_handler.add_error(error)
      true
    end
  end
end
```

Gitのバージョンが違うのかgit statusした時のメッセージが違うため、あのエラーメッセージが表示されているようです。
現在の環境のGitのバージョンは1.7.1です。バージョンアップした方がいいでしょうが、面倒くさいのでソースコードいじりました。（そっちも面倒くさいだろ！というツッコミはなしです

```
【変更前】
return false if status.include?("nothing to commit, working directory clean")
【変更後】
return false if status.include?("nothing to commit (working directory clean)")
```

それでは気を取り直して。

```
[root@localhost react_on_rails_sample]# bundle exec rails generate react_on_rails:install -S
Running via Spring preloader in process 2857
       route  get 'hello_world', to: 'hello_world#index'
      create  client/assets
      create  client/assets/stylesheets
      create  client/assets/fonts
      create  client/assets/fonts/.keep
      create  client/assets/images
      create  client/assets/images/.keep
      append  .gitignore
     prepend  app/assets/javascripts/application.js
        gsub  app/assets/javascripts/application.js
        gsub  app/assets/javascripts/application.js
        gsub  app/assets/javascripts/application.js
        gsub  app/assets/javascripts/application.js
      create  client/app/bundles/HelloWorld/components
      create  client/app/bundles/HelloWorld/containers
      create  client/app/bundles/HelloWorld/startup
      create  app/controllers/hello_world_controller.rb
      create  config/initializers/react_on_rails.rb
      create  client/.babelrc
      create  client/index.jade
      create  client/server.js
      create  client/webpack.client.base.config.js
      create  client/webpack.client.rails.config.js
      create  REACT_ON_RAILS.md
      create  client/REACT_ON_RAILS_CLIENT_README.md
      create  package.json
      create  Procfile.dev
      create  Procfile.dev-hot
      create  app/views/hello_world/index.html.erb
      create  client/app/bundles/HelloWorld/components/HelloWorldWidget.jsx
      create  client/webpack.client.hot.config.js
      create  client/package.json
      append  Gemfile
      create  client/app/bundles/HelloWorld/startup/clientRegistration.jsx
      create  client/webpack.server.rails.config.js
      create  client/app/bundles/HelloWorld/startup/serverRegistration.jsx
      create  lib/tasks/linters.rake
      create  lib/tasks/assets.rake
      append  config/initializers/assets.rb
      create  client/app/bundles/HelloWorld/containers/HelloWorld.jsx
      create  client/app/bundles/HelloWorld/startup/HelloWorldAppServer.jsx
      create  client/app/bundles/HelloWorld/startup/HelloWorldAppClient.jsx
      create  client/.eslintrc
      create  client/.eslintignore
      create  client/.jscsrc
      create  app/assets/stylesheets/_bootstrap-custom.scss
      create  client/assets/stylesheets/_post-bootstrap.scss
      create  client/assets/stylesheets/_pre-bootstrap.scss
      create  client/assets/stylesheets/_react-on-rails-sass-helper.scss
      create  client/bootstrap-sass.config.js
      append  app/assets/stylesheets/application.scss
        gsub  app/assets/stylesheets/application.scss
        gsub  app/assets/stylesheets/application.scss
      append  Gemfile
      append  app/assets/javascripts/application.js

What to do next:

  - Ensure your bundle and npm are up to date.

      bundle && npm i

  - Run the npm rails-server command to load the rails server.

      npm run rails-server

  - Visit http://localhost:3000/hello_world and see your React On Rails app running!
```

今度は上手くいきました。
次にやることは、What to do nextに書いてあるのでその通りに実行していきます。

```
[root@localhost react_on_rails_sample]# bundle && npm i
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
installing your bundle as root will break this application for all non-root
users on this machine.
Fetching gem metadata from https://rubygems.org/...........
・・・
Bundled gems are installed into ./tmp/bundler.
npm WARN lifecycle react-webpack-rails-tutorial@1.1.1~postinstall: cannot run in wd %s %s (wd=%s) react-webpack-rails-tutorial@1.1.1 cd client && npm install /var/rails/react_on_rails_sample
```

またエラーです。
bundle installには成功したようですが、その後のnpm installに失敗したようです。
念のためnpm installだけ実行してみます。

```
[root@localhost react_on_rails_sample]# npm install
npm WARN lifecycle react-webpack-rails-tutorial@1.1.1~postinstall: cannot run in wd %s %s (wd=%s) react-webpack-rails-tutorial@1.1.1 cd client && npm install /var/rails/react_on_rails_sample
```

エラーは特に変わらないですね。。
調べてみましたが、なぜpostinstallが失敗しているかの原因と解決策が分からなかったので、とりあえずpostinstallが実行するコマンドをそのまま実行してみます。
※原因と解決方法分かったのでコメントに書きました。

```
[root@localhost react_on_rails_sample]# cd client && npm install
npm WARN install Couldn't install optional dependency: Unsupported
npm WARN deprecated bootstrap-sass-loader@1.0.10: Replaced by bootstrap-loader. Please upgrade.
npm WARN deprecated npmconf@2.1.2: this package has been reintegrated into npm and is now out of date with respect to npm
npm WARN deprecated lodash@1.0.2: lodash@<3.0.0 is no longer maintained. Upgrade to lodash@^4.0.0
npm WARN prefer global jsonlint@1.6.2 should be installed with -g
npm WARN prefer global node-gyp@3.2.1 should be installed with -g

・・・
```

ちょっとWARNが出てますが、大方インストールできたようです。(WARNは後で解決しましょう。。
そして、次のコマンドnpm run rails-serverを実行します。

```
[root@localhost react_on_rails_sample]# npm run rails-server

> react-webpack-rails-tutorial@1.1.1 rails-server /var/rails/react_on_rails_sample
> echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev

visit http://localhost:3000/hello_world
sh: foreman: コマンドが見つかりません

npm ERR! Linux 2.6.32-573.12.1.el6.x86_64
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "rails-server"
npm ERR! node v5.4.1
npm ERR! npm  v3.3.12
npm ERR! file sh
npm ERR! code ELIFECYCLE
npm ERR! errno ENOENT
npm ERR! syscall spawn
npm ERR! react-webpack-rails-tutorial@1.1.1 rails-server: `echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev`
npm ERR! spawn ENOENT
npm ERR! 
npm ERR! Failed at the react-webpack-rails-tutorial@1.1.1 rails-server script 'echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the react-webpack-rails-tutorial package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev
npm ERR! You can get their info via:
npm ERR!     npm owner ls react-webpack-rails-tutorial
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /var/rails/react_on_rails_sample/npm-debug.log
```

またもや盛大にエラーが出てます。
「sh: foreman: コマンドが見つかりません」と出ているので、foremanがインストールされていないようです。
一応Gemfile.lockを調べてみます。

```
[root@localhost react_on_rails_sample]# cat Gemfile.lock | grep foreman
```

インストールされていないですね。。
Gemfileに追加してインストールします。（rails generate react_on_rails:installした時に追加してくれないんですね。。。
開発でしか使わないので、group :development doにでも書いておきましょう。

```ruby:Gemfile
・・・
group :development do
  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'foreman'　←追加
end
・・・
```

bundle installが完了したら改めてnpm run rails-serverを実行してみましょう。

```
[root@localhost react_on_rails_sample]# npm run rails-server

> react-webpack-rails-tutorial@1.1.1 rails-server /var/rails/react_on_rails_sample
> echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev

visit http://localhost:3000/hello_world
sh: foreman: コマンドが見つかりません
・・・
```

・・・そうでした。。gemをプロジェクトのローカルにインストールしているので通常のforemanコマンドは使えません。bundler execが必要です。
なので、npm runで実行するrails-serverを修正します。

```json:package.json
{
  "name": "react-webpack-rails-tutorial",
  "version": "1.1.1",
  ・・・
  "scripts": {
    "postinstall": "cd client && npm install",
    "test": "rspec && (cd client && npm run lint)",
    "express-server": "echo 'visit http://localhost:4000' && cd client && npm start",
    "rails-server": "echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev"
　　↓foremanの直前にbundle execを追加
    "rails-server": "echo 'visit http://localhost:3000/hello_world' && bundle exec foreman start -f Procfile.dev"
  },
・・・
```

変更後、再度npm run rails-serverを実行します。

```
[root@localhost react_on_rails_sample]# npm run rails-server

> react-webpack-rails-tutorial@1.1.1 rails-server /var/rails/react_on_rails_sample
> echo 'visit http://localhost:3000/hello_world' && bundle exec foreman start -f Procfile.dev

visit http://localhost:3000/hello_world
23:03:12 web.1    | started with pid 3360
23:03:12 client.1 | started with pid 3361
23:03:12 server.1 | started with pid 3363
23:03:12 client.1 | rm: cannot remove `app/assets/javascripts/generated/*': そのようなファイルやディレクトリはありません
23:03:13 server.1 | 
23:03:13 server.1 | > react-webpack-rails-tutorial@1.1.0 build:dev:server /var/rails/react_on_rails_sample/client
23:03:13 server.1 | > webpack -w --config webpack.server.rails.config.js
23:03:13 server.1 | 
23:03:13 client.1 | 
23:03:13 client.1 | > react-webpack-rails-tutorial@1.1.0 build:dev:client /var/rails/react_on_rails_sample/client
23:03:13 client.1 | > webpack -w --config webpack.client.rails.config.js
23:03:13 client.1 | 
23:03:14 client.1 | Webpack dev build for Rails
23:03:17 web.1    | [2016-02-09 23:03:17] INFO  WEBrick 1.3.1
23:03:17 web.1    | [2016-02-09 23:03:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
23:03:17 web.1    | [2016-02-09 23:03:17] INFO  WEBrick::HTTPServer#start: pid=3360 port=3000
23:03:23 server.1 | Hash: 8d83dd48f344ad6f63e2
23:03:23 server.1 | Version: webpack 1.12.13
23:03:23 server.1 | Time: 9435ms
23:03:23 server.1 |            Asset     Size  Chunks             Chunk Names
23:03:23 server.1 | server-bundle.js  1.84 MB       0  [emitted]  main
23:03:23 server.1 |    [0] multi main 40 bytes {0} [built]
23:03:23 server.1 |     + 649 hidden modules
23:03:27 client.1 | Hash: 9c2e8dbbd6027ec29bd6
23:03:27 client.1 | Version: webpack 1.12.13
23:03:27 client.1 | Time: 13139ms
23:03:27 client.1 |            Asset     Size  Chunks             Chunk Names
23:03:27 client.1 |    app-bundle.js  4.49 MB       0  [emitted]  app
23:03:27 client.1 | vendor-bundle.js  1.54 MB       1  [emitted]  vendor
23:03:27 client.1 |    [0] multi app 28 bytes {0} [built]
23:03:27 client.1 |    [0] multi vendor 76 bytes {1} [built]
23:03:27 client.1 |     + 655 hidden modules
```

【VirtualBoxなど利用している場合】
上記でサーバーの起動まで完了しましたが、今回はVirtualBoxに作った環境にホストOSからアクセスしていたのでもう一手間必要でした。

```ruby:Procfile.dev
【変更前】
web: rails s
client: sh -c 'rm app/assets/javascripts/generated/* || true && cd client && npm run build:dev:client'
server: sh -c 'cd client && npm run build:dev:server'

【変更後】
web: rails s -b 0.0.0.0
client: sh -c 'rm app/assets/javascripts/generated/* || true && cd client && npm run build:dev:client'
server: sh -c 'cd client && npm run build:dev:server'
```

外部からアクセスする場合は、rails sで -b 0.0.0.0を指定する必要があるので、上記のように修正しました。


これで、http://localhost(またはIP):3000/hello_world
にアクセスすると下記のような画面が表示されるようになると思います。

![ReactOnRailsSample.png](https://qiita-image-store.s3.amazonaws.com/0/97493/0a5a5800-56f7-31e6-3a14-c1debece5b96.png)

サーバーサイドレンダリング出来ているかは、ブラウザのJavascriptを無効にしてからアクセスするとちゃんと表示されるので、サーバーサイドレンダリングが出来ているのが確認できると思います。