Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
123
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@suusan2go

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

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

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

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
123
Help us understand the problem. What are the problem?