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

react(flux)とrails(turbolinks)の親和性

More than 3 years have passed since last update.

1. 概要

この記事はQiita React.js Advent Calendar 2015の25日目に投稿させて頂いた記事です.
react(flux)とrails(turbolinks)を組み合わせるとどのような利点があるのか,ということについて書きたいと思います.
端的に言うと複数ページに渡るものでどういう風にreactの良さを活かしていくか,という記事になります.

2. turbolinksについて

turbolinksはajaxを用いてページ遷移を行ってくれる機能で,Rails4からデフォルト搭載されています.
動作としてはajaxでDOMだけを更新し,jsやcssの取得し直しはしない,といった具合になっています.

この機能は,様々な問題も発生するのですが(例えばTwitterのwidgetが発火しないとか),react(flux)とはとても相性が良い機能です.
fluxを用いた場合,ajax(fetch)で取得されたデータはStoreに保存され,それに基いて描画を行うため,ページ遷移時のjsの読み直しはないほうがありがたいためです.
こちらはjsがプリコンパイルされ,1つのファイル(どのページでも同じ)になっていることが前提の話となりますが,railsの場合はそのようになっています.

更に詳細を知りたい方は,以下の記事が参考になったのでお勧めしておきます.

3. 具体的にやりたいこと

私が具体的にやりたかったことは,Storeの情報を維持したままページ遷移することです.
通常であればreact-routerを使うところなのですが,railsにデフォルトで入っているturbolinksを使うことで同じようなことが実現できます.

どのページから見られてもWebページに必要なコンテンツを非同期でStoreに格納しておき,
ユーザに対してページ遷移の時間で不満を感じさせない,ということがやりたいことでした.
例えばコントローラ側の処理に1000msかかるような重いページであれば,非同期でロードしておき,renderコストのみに抑えるといったことです.
最初に重いページを開かれた場合はどうしようもないのですが,トップから重いページに移動する際などに役立つと思います.

4. TODO App

実際に簡単なTODO Appを作りました.

ob.gif

fetchでGETしているtodos.jsonがStoreにデータを格納しているところです.
最初のページロード以外では一覧を取得していないということが見にくいですが,わかると思います.
このようにturbolinksを上手く使うとAPIを叩く量も減らすことができ,非常に良いなと感じました.

このAppではTODOのstateが空だった時のみ現状の全てのTODOを取得し,それ以外の時は取得し直しは一切行わないようにしています.

TodoActionCreators.fetch() if Object.keys(@state.todos).length is 0

利用した主なgemは,

  • react-rails
  • bower-rails
  • sprockets-coffee-react

の3点です.

react-rails

railsでreactを使う際に便利なgemです.
server側でのprerenderができたり,スネーク<=>キャメルの変数名の変換や,development/productionモードなどの切り替えもできます.

bower-rails

フロント系ライブラリの管理を行ってくれます.
rails-assetsと違ってフロント系ライブラリで画像などが使われていた場合にも対応しているので,
productionを踏まえて利用するならbower-rails一択かなと思っています.

今回はfluxとeventemitterをインストールするために利用します.

sprockets-coffee-react

cjsxでreactを書くためのgemです.
jsxのまま書きたいという方は不要かもしれません.

デフォルトでもcoffeescriptの対応はされているのですが,render部分を

  render: ->
    `<div></div>`

といった感じで`で囲わないといけないのが面倒なので使っています.

5. 実際色々作るための環境準備

Rails 4.2.5,Ruby 2.2.3での環境準備となります.
まずは,railsをnewしてからライブラリ周りの準備を整えていきます.

rails new flux-rails -T --skip-bundle
vim Gemfile

Gemfileを開いたら以下の4つを追加します.
slim以外のテンプレートエンジンを使いたい方は適宜置き換えて下さい.

gem 'react-rails'
gem 'sprockets-coffee-react'
gem 'bower-rails'
gem 'slim-rails'

追加した後はbundle install

bundle install -j4 --path vendor/bundle

次に,フロント系ライブラリの準備をします.

rails g bower_rails:initialize
vim Bowerfile

Bowerfileにfluxとeventemitterを追加.
reduxなどもありますが,公式のfluxを利用しています.
今回はeventemitter2の方を利用します.

asset 'flux'
asset 'eventemitter2'

終わった後はrake bower:installでインストール完了です.
最後に,jsファイルを読み込ませます.
react系の読み込みは,turbolinksの後に書かないとページ遷移時に正しくロードされないので注意が必要です.

app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require flux/dist/Flux
//= require eventemitter2/lib/eventemitter2
//= require_tree ./constants
//= require_tree ./dispatcher
//= require_tree ./actions
//= require_tree ./stores
//= require_tree ./components

require_treeではディレクトリがないとエラーが出るのでとりあえず,ディレクトリも生成しておきます.

cd app/assets/javascripts/
mkdir constants
mkdir dispatcher
mkdir actions
mkdir stores
mkdir components

これで環境構築としては終了です.
後は普通にreactを書くときのように記述していけばOKです.

6. react-railsの利用しているモノ

componentをrender

通常のrenderは

= react_component('HelloMessage', {name: 'John'})

サーバサイドでrenderingさせたい場合はこんな感じ.

= react_component('HelloMessage', {name: 'John'}, {prerender: true})

name: 'John' はpropsに渡る値となります.
何のtagでrenderするか,class名は何にするか,というのもここで指定が行えます.

キャメル<=>スネークに変数名を変換

config/application.rbconfig.react.camelize_props = trueという記述をしておくと,
キャメル<=>スネークの変数名変換を自動で行ってくれます.
todo_id: 4をpropsで渡すと,@props.todoId => 4といった感じにしてくれ,地味に便利です.

controllerでrender

class TodoController < ApplicationController
  def index
    @todos = Todo.all
    render component: 'TodoList', props: { todos: @todos }, tag: 'span', class: 'todo'
  end
end

コントローラ側でもサーバサイドでのrenderingが行えます.

その他として,自分はcoffeeで書いているのでよく知りませんが,ES6で書くこともできるようです.

7. まとめ

  • turbolinksを使うとrailsのrailsに乗ったままreact(flux)が利用できる
  • react-railsは便利
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした