8日目 に続けての投稿です。
今回は React and Redux を Rails Engine でクロスさせる開発を紹介します。
Rails Engine は Rails に Webアプリケーションの部品として機能を追加できる仕組みです。
React and Redux もコンポーネントを組合せて機能を作ります。
- Rails Engine: Web Application Component Framework
- React and Redux: Web Client Component Framework
React and Redux の コンポーネント を Rails Engine でさらに コンポーネント にする仕組みだと
関心事の分離して設計できて コンポーネント指向 が捗ります。
サンプルはこちらです。
Environment
- Ruby: v2.2.3
- Rails: v4.2.5
- React: v0.14.0
- Redux: v3.0.4
Rails
まずは、Rails に Virtual DOM をレンダリングするためのサーバーサイドを構築します。
Setup
-
rails plugin new
のコマンドで Rails Engine を作成します。 -
--mountable
のオプションで Rails の部品として開発できます。 -
foreman
は後で Node.js と Rails を起動するのに利用します。
rails plugin new rails_engine_react_example --mountable \
--skip-javascript \
--skip-bundle
$ cd rails_engine_react_example/
$ echo "gem 'foreman', require: false" >> Gemfile
$ bundle
Making
-
engine.rb
にgenerate
コマンドのデフォルトを記述します。
module RailsEngineReactExample
class Engine < ::Rails::Engine
isolate_namespace RailsEngineReactExample
config.generators do |g|
g.javascripts false
g.stylesheets false
g.helper false
end
end
end
-
rails generate controller
コマンドで controller と view を作ります。
$ rails generate controller main index
-
routes.rb
でmain_controller
をroot
に設定します。 -
*path
は Tips の Redux Router で利用しています。
RailsEngineReactExample::Engine.routes.draw do
root 'main#index'
get '*path', to: 'main#index'
end
-
index.html.erb
に Virtual DOM がレンダリングされるid="main"
を記述します。
<div id="main"></div>
-
application_helper.rb
に stylesheet と javascript の参照先を切替えるメソッドを追加します。
module RailsEngineReactExample
module ApplicationHelper
def switch_stylesheet_link_tag(path)
path = "http://localhost:4000/assets/stylesheets/#{path}.css" if ENV["DEBUG"]
stylesheet_link_tag(path)
end
def switch_javascript_include_tag(path)
path = "http://localhost:4000/assets/javascripts/#{path}.js" if ENV["DEBUG"]
javascript_include_tag(path)
end
end
end
-
application.html.erb
に先ほどのヘルパーのメソッドを記述します。
<!DOCTYPE html>
<html>
<head>
<title>RailsEngineReactExample</title>
<%= switch_stylesheet_link_tag "rails_engine_react_example/application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
<%= switch_javascript_include_tag "rails_engine_react_example/application" %>
</body>
</html>
Run
-
rails server
コマンドで起動してhttp://localhost:3000/rails_engine_react_example
にアクセスします。
真っ白ですね... 安心してください、 Virtual DOM はこれからです。
Node.js
次は、Node.js で Virtual DOM をレンダリングするためのフロントエンドを構築します。
Setup
- Node.js は /private/static に設置します。
- yo redux のコマンドで React and Redux が構築できます。
- Node.js は 4000 ポートを利用します。
$ mkdir -p ./private/static
$ cd ./private/static
$ yo redux
? What's the name of your application? static
? Describe your application in one sentence: ...
? Which port would you like to run on? 4000
? Install dependencies? No
-
js
はapp
にリネームします。(css
も components で記述する流れですね。) -
package.json
ファイルはルートディレクトリにコピーします。
$ mv js app
$ cd ../../
$ cp ./private/static/package.json .
$ echo /node_modules >> .gitignore
$ npm install
-
package.json
のscripts
のコマンドにcd ./private/static &&
を付加します。
"scripts": {
"start": "cd ./private/static && DEBUG=true node server.js",
"build": "cd ./private/static && webpack -p --config webpack.production.js",
"build-dev": "cd ./private/static && webpack -p"
},
-
webpack.config.js
を Rails のヘルパーのメソッドで参照できるように調整します。
entry: [
'webpack-dev-server/client?http://localhost:4000',
'webpack/hot/only-dev-server',
'./app/index.js'
],
output: {
path: __dirname + '/assets/',
publicPath: 'http://localhost:4000/assets/',
filename: 'javascripts/rails_engine_react_example/application.js',
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
devFlagPlugin,
new ExtractTextPlugin('stylesheets/rails_engine_react_example/application.css')
],
-
webpack.production.js
を Rails のapp/assets/
に出力するように調整します。
entry: [
'./app/index.js'
],
output: {
path: __dirname + '../../../app/assets/',
publicPath: '/static/',
filename: 'javascripts/rails_engine_react_example/application.js',
},
plugins: [
new webpack.NoErrorsPlugin(),
devFlagPlugin,
new ExtractTextPlugin('stylesheets/rails_engine_react_example/application.css')
],
Run
- Rails Engine は Rails にマウントしている状態の
dummy
が提供されています。 -
foreman
の Procfile に Rails と Node.js の起動コマンドを追加します。 -
DEBUG=true foreman start
コマンドで起動します。
$ cd test/dummy/
$ echo "web: rails server" >> Procfile
$ echo "pack: npm start" >> Procfile
$ DEBUG=true foreman start
http://localhost:3000/rails_engine_react_example
にアクセスすると Redux のサンプルが表示されます。
-
npm run build
コマンドでapplication.js
とapplication.css
が生成されます。 -
rails server
コマンドで起動します。
$ npm run build
$ rails server
http://localhost:3000/rails_engine_react_example
にアクセスすると Redux のサンプルが表示されます。
これで Node.js X Rails Engine の基本の準備ができました。
View は React に任せて Rails は View にあまり関心を持たないほうがいいですね。
Tips
Redux Router
Rails は Router もあまり関心を持たないほうが...
Setup
-
react-router
とredux-simple-router
を利用します。
$ npm install react-router --save
$ npm install redux-simple-router --save
Making
コードは一部ですが Router や Link を記述します。
-
App.js
に Router を記述します。
<Router history={history}>
<Route path="/" component={Home}>
<Route path="parent" component={Parent}>
<Route path="child" component={Child} />
<Route path="child/:id" component={Child} />
</Route>
</Route>
</Router>
-
Home.js
に Link を記述します。
<dev>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/parent">parent</Link></li>
<li><Link to="/parent/child">child</Link></li>
<li><Link to="/parent/child/1">child 1</Link></li>
<li><Link to="/parent/child/2">child 2</Link></li>
<li><Link to="/parent/child/3">child 3</Link></li>
</ul>
</dev>
Run
Link のエリアをクリックとサーバーサイドにはリクエストが送信されませんが、レンダリングされます。
URLを入力するとサーバーサイドにはリクエストが送信されますが、Rails に *path
を設定しているので Virtual DOM がレンダリングされます。
Redux は状態を管理するメカニズムがあります。ページ遷移も状態として管理されます。
Rails は MVC2 ですが、 View に状態が管理されるので MVC ですね。
Enjoy Node.js X Rails Engine