流行りのfluxフレームワークreduxとreact-railsを試してみる。
reduxのexamplesにある、counterをreact-railsを使って書いてみます。
https://github.com/rackt/redux/tree/master/examples/counter
※以下のバージョンで書いてます。
- rails 4.2.3
- redux 3.0.4
- react-rails 1.4.2
- react 0.14.2
ソースは以下におきました。
ソースは@odk211さんからPR頂いてbabel6を使ったものになってます。
https://github.com/suzan2go/react-rails-redux-sample
事前準備
browserify-railsのインストール
Gemfile
に以下を追加してbundle install
しましょう。
gem 'browserify-rails'
reduxのサンプルはES6の書き方を沢山使ってますので、そのまま使うにはトランスパイラが必要になります。config/application.rb
に以下を追加しましょう
config.browserify_rails.commandline_options = '-t babelify'
browserify
とbabelify
、そしてbrowserify-rails
に必要なbrowserify-incremental
をnpmで入れます。
npm install --save-dev browserify
npm install --save-dev browserify-incremental
npm install --save-dev babelify
babel6で動かす場合
babelify 7.0 以降を入れる場合など、babel6で動かす場合は以下も必要になるようです
npm install --save-dev babel-preset-es2015
npm install --save-dev babel-preset-react
npm install --save-dev babel-preset-stage-2
※ babel-preset-stage-2
自体はなくても動かせるのですが、今回扱うサンプルの.babelrc
にstage-2
の記述があったので、一応追加しています。
railsのアプリケーションrootに、.babelrc
を追加します。
{
"presets": ["es2015", "stage-2", "react"]
}
reduxのインストール
今回のexampleでは以下が必要になるのでnpm install
します。1.0.0-rc
よりredux
の中でもreact
に特化した部分がreact-redux
に切りだされています。react
を使う場合はこちらも必要です。
https://github.com/rackt/redux/releases/tag/v1.0.0-rc
npm install --save react
npm install --save redux
npm install --save react-redux
npm install --save redux-thunk
react-rails
でもreact
が入りますが、今回はnpmで入れたreact
を使うことにします。
react-railsのインストール
なにはともあれ、react-rails
をインストールします。Gemfile
に以下を追加してbundle install
しましょう。
gem 'react-rails'
インストールが完了したら以下のコマンドを実行します。
rails g react:install
このコマンドを実行すると以下のファイル、ディレクトリが追加され、application.js
に記述が追加されます。
app/assets/javascripts/components.js
app/assets/javascripts/components/
application.js
は多分こんな感じになってるはず。
# application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require components
//= require_tree .
npmでいれたreact
を使うので、//=require react
は消しても大丈夫です。//= require_tree .
は不要なので消しておきましょう。
Reduxのexamplesにあるcounterをreact-railsで動かす
前置きが長くなりましたが、いよいよreact-railsでreduxを動かしてみます。
reduxのexamplesを持ってくる
redux
のレポジトリに入っているexaplesから以下のフォルダをapp/assets/javascripts/components/
配下に格納します。ファイルは他にもありますが、とりあえずこれだけあれば動かせます。
- actions
- components
- containers
- reducers
- store
babel6で動かす場合
公式のcounterのサンプルと同じディレクトリにある.babelrc
があると、babel6では動きません。サンプルの.babelrc
は削除して、前述の通りrailsのアプリケーションrootに.babelrc
を作成してください。(この.babelrc
を同じように書き換えても動きます。)
{
"presets": ["es2015", "stage-2", "react"]
}
components.jsを書き換える
react-rails
をインストールしたときに作成したcomponents.js
を書き換えます。
Root.js
に定義されているRoot
がカウンターアプリのコンポーネントなので、これをreact-rails
のreact_component
ヘルパから呼べるようにします。
window.React = require('react');
window.React = require('react-dom');
window.Root = require('./components/containers/Root.js');
// babel6の場合
// window.Root = require('./components/containers/Root.js').default;
Viewファイルから呼び出す
Viewファイルにreact_component
ヘルパーを記述してブラウザからアクセスしてみると。
<%= react_component('Root') %>
こんな感じで表示されるようになるはず!
react_componentヘルパーに渡したパラメータで初期値をセットできるようにする
react_componentヘルパーに初期値を渡して、サーバサイドレンダリングできるようにしてみます。
Viewファイルを書き換える
まず先ほど記述したViewファイルを以下の様に書き換えます。素のReactコンポーネントの場合はこれで値を渡すことがますが、今回はredux
を通しているのでこれだけでは値はセットされません。
<%= react_component('Root', {counter: 10}) %>
カウンター値をセットするactionを定義する
actions/counter.js
に以下を追記します。
export const SET_COUNTER = 'SET_COUNTER';
export function setCounter(counter) {
return {
type: SET_COUNTER,
counter: counter
};
}
カウンター値をセットするstoreを定義する
reducers/counter.js
に以下を追記します。(redux
では他のフレームワークでstore
となっているものをreducer
と呼ぶようです。)
// SET_COUNTERを追記
import { INCREMENT_COUNTER, DECREMENT_COUNTER, SET_COUNTER } from '../actions/counter';
export default function counter(state = 0, action) {
switch (action.type) {
case INCREMENT_COUNTER:
return state + 1;
case DECREMENT_COUNTER:
return state - 1;
// SET_COUNTERに対応する処理を追加します。
case SET_COUNTER:
return action.counter;
default:
return state;
}
}
componentWillMountからsetCountを呼ぶ
containers/Root.js
に以下を追記します。サーバサイドレンダリングを行う場合、サーバ側で行う処理はReactコンポーネントのライフサイクルの中でもcomponentWillMount
に記述する必要があります。
https://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount
import {setCounter} from '../actions/counter'
export default class Root extends Component {
componentWillMount() {
store.dispatch(setCounter(this.props.counter));
}
// 以下省略
これで準備は完了です!もう一度アクセスするとこんなかんじになるはず。
サーバサイドレンダリング
react-rails
を使う大きなメリットですね。ヘルパーに{prerender: true}
とするだけでサーバサイドでのレンダリングが可能になります。試しにブラウザでjavascriptを無効にしても、最初に表示される画面は同じになるはずです。
<%= react_component('Root', {counter: 10}), {prerender: true} %>
感想・所感
react-railsとfluxについて書いてある情報が少なくて困った。react_component
経由で値を渡すところとか結構手探りなので、flux的におかしいとかあればご指摘ください…
redux以外も結構そうみたいなんですが普通にES6、なんならES7の記法で当たり前のように書いてあるものが多くて、今のプロジェクトやサービスでは使わないにしてもES6読めるようにしとく必要があるなーと感じました。
reactもfluxについても今回はじめてちゃんと勉強したので、react、flux、ES6全部あんまり良くわかっていないところからのスタートはかなりきつかったです。