Help us understand the problem. What is going on with this article?

react_on_rails導入

More than 3 years have passed since last update.

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
satzz
Web Engineer 2009-2011 Hatena(part-time) 2011-2015 DeNA
http://satzz.me
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away