Ruby
Rails
React
react-rails
redux

railsでreact(react-rails)とreduxを動かす

More than 3 years have passed since last update.

流行りの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'

browserifybabelify、そして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自体はなくても動かせるのですが、今回扱うサンプルの.babelrcstage-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

https://github.com/rackt/redux/tree/master/examples/counter


babel6で動かす場合

公式のcounterのサンプルと同じディレクトリにある.babelrcがあると、babel6では動きません。サンプルの.babelrcは削除して、前述の通りrailsのアプリケーションrootに.babelrcを作成してください。(この.babelrcを同じように書き換えても動きます。)

{

"presets": ["es2015", "stage-2", "react"]
}


components.jsを書き換える

react-railsをインストールしたときに作成したcomponents.jsを書き換えます。

Root.jsに定義されているRootがカウンターアプリのコンポーネントなので、これをreact-railsreact_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') %>

こんな感じで表示されるようになるはず!

Kobito.vSZjlv.png


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));
}
// 以下省略

これで準備は完了です!もう一度アクセスするとこんなかんじになるはず。

Kobito.n23TSI.png


サーバサイドレンダリング

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全部あんまり良くわかっていないところからのスタートはかなりきつかったです。