• 7
    いいね
  • 0
    コメント

react_on_rails

RailsでReactを使う場合に検討するgemとして、導入が容易なのはreact-rails。一方、react_on_railsはWebpack, Babel, React, Redux, React-RouterなどのモダンJSスタックをRailsと密結合にならない形で導入したい状況を想定しているようだ。

react_on_railsを使った新規アプリをHerokuへデプロイするフローを確認する。

環境

  • Ruby 2.2.3
  • Rails 4.2

ローカルで動かす

空のアプリ作成

$ rails new APP_NAME --database=postgresql
$ cd APP_NAME
$ rake db:create
$ rails s
$ git init
$ git add .
$ git commit -m'initial'

react_on_rails導入

+gem "react_on_rails", "~> 6"
$ bundle

git commit

ここでの注意点は、次に実行するrails g react_on_rails:installは、git statusの結果が綺麗になっていないとこける仕様になっていること。なので一旦commitする必要がある。

$ git status
$ git add .
$ git commit -m'install react_on_rails'

react_on_rails起動

$ rails g react_on_rails:install

サンプルコードを含めていろいろファイルが自動修正・作成される。

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   .gitignore
        modified:   Gemfile
        modified:   Gemfile.lock
        modified:   app/assets/javascripts/application.js
        modified:   config/initializers/assets.rb
        modified:   config/routes.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        Procfile.dev
        app/controllers/hello_world_controller.rb
        app/views/hello_world/
        client/
        config/initializers/react_on_rails.rb
        package.json

少し複雑なので、主なファイルを追っておく。

Webpackでコンポーネントをバンドルするための変更

package.json
{
  "name": "react-webpack-rails-tutorial",
  "version": "0.0.1",
  "engines": {
    "node": "5.10.0",
    "npm": "3.5.0"
  },
  "scripts": {
    "postinstall": "cd client && npm install",
    "rails-server": "echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev",
    "test": "rspec"
  }
}
Procfile.dev
web: rails s -p 3000
client: sh -c 'rm app/assets/webpack/* || true && cd client && npm run build:development'
client/package.json
{
  "name": "react-webpack-rails-tutorial",
  "version": "0.0.1",
  "engines": {
    "node": "5.10.0",
    "npm": "3.5.0"
  },
  "scripts": {
    "build:test": "webpack --config webpack.config.js",
    "build:production": "NODE_ENV=production webpack --config webpack.config.js",
    "build:development": "webpack -w --config webpack.config.js"
  },
..
client/webpack.config.js
const webpack = require('webpack');
const path = require('path');

const devBuild = process.env.NODE_ENV !== 'production';
const nodeEnv = devBuild ? 'development' : 'production';

const config = {
  entry: [
    'es5-shim/es5-shim',
    'es5-shim/es5-sham',
    'babel-polyfill',
    './app/bundles/HelloWorld/startup/HelloWorldApp',
  ],

  output: {
    filename: 'webpack-bundle.js',
    path: '../app/assets/webpack',
  },

..

Reactコンポーネントとエントリポイント

client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx
import React from 'react';
import ReactOnRails from 'react-on-rails';

import HelloWorld from '../containers/HelloWorld';

const HelloWorldApp = (props) => (
  <HelloWorld {...props} />
);

// This is how react_on_rails can see the HelloWorldApp in the browser.
ReactOnRails.register({ HelloWorldApp });
client/app/bundles/HelloWorld/containers/HelloWorld.jsx
import React, { PropTypes } from 'react';
import HelloWorldWidget from '../components/HelloWorldWidget';

// Simple example of a React "smart" component
export default class HelloWorld extends React.Component {
  static propTypes = {
    name: PropTypes.string.isRequired, // this is passed from the Rails view
  };
  ..
}

WebpackでバンドルしたJSを使うための変更

config/initializers/react_on_rails.rb
ReactOnRails.configure do |config|
  config.generated_assets_dir = File.join(%w(app assets webpack))
  config.webpack_generated_files = %w( webpack-bundle.js )
  config.server_bundle_js_file = "webpack-bundle.js"
  config.npm_build_test_command = "npm run build:test"
  config.npm_build_production_command = "npm run build:production"
  config.prerender = false
  config.trace = Rails.env.development?
  config.development_mode = Rails.env.development?
  config.replay_console = true
  config.logging_on_server = true
  config.raise_on_prerender_error = false # change to true to raise exception on server if the JS code throws
  config.server_renderer_pool_size = 1 # increase if you're on JRuby
  config.server_renderer_timeout = 20 # seconds
  config.skip_display_none = false
  config.server_render_method = "ExecJS"
  config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg|map)/
end
config/initializers/assets.rb
+Rails.application.config.assets.paths << Rails.root.join("app", "assets", "webpack")
app/assets/javascripts/application.js
+//= require webpack-bundle

Railsアプリケーションの変更

config/routes.rb
+  get 'hello_world', to: 'hello_world#index'
app/controllers/hello_world_controller.rb
class HelloWorldController < ApplicationController
  def index
    @hello_world_props = { name: "Stranger" }
  end
end
app/views/hello_world/index.html.erb
<h1 class="alert alert-info this-works">Hello World</h1>
<%= react_component("HelloWorldApp", props: @hello_world_props, prerender: false) %>

起動

さらに必要なライブラリをインストールする。

$ bundle && npm i

起動

$ npm run rails-server

> react-webpack-rails-tutorial@0.0.1 rails-server /Users/satzz/APP_NAME
> echo 'visit http://localhost:3000/hello_world' && foreman start -f Procfile.dev

visit http://localhost:3000/hello_world
..

シーケンスは次の通り。

  1. package.jsonからforemanを起動
    • foremanはProcfileベースでアプリケーション管理するRuby製ツール
      • JSのコードベースの変更を監視して文法エラーなどの警告も行う
  2. foremanがProcfile.devを読みRailsサーバーを起動、同時にclient/package.jsonをたどってwebpackを起動
  3. webpackが、webpack.config.jsに書かれたHelloWorldApp.jsxをエントリポイントにJSをバンドルし、 ./app/assets/webpack/webpack-bundle.jsを生成
    • 生成されたファイルは.gitignoreされている
  4. Railsが、生成されたwebpack-bundle.jsを使う
    • Railsとのインターフェースに使われる(=react_componentで呼ばれる)全てのReactコンポーネントが、ReactOnRails.registerを使って一意な名前で公開されている必要がある

動作確認

http://localhost:3000/hello_world
で動作確認する。

Screen Shot 2016-11-01 at 11.13.56.png

Herokuで動かす

$ git add .
$ git commit -m'make react work'

$ heroku create

Buildpackの順番調整

$ heroku buildpacks:clear --app dark-wizard-97318
$ heroku buildpacks:add heroku/nodejs --app dark-wizard-97318
$ heroku buildpacks:add heroku/ruby --app dark-wizard-97318

デプロイ

$ git push https://git.heroku.com/dark-wizard-97318.git master