すでに動いているRailsアプリケーションにReact.jsを投入する

  • 66
    いいね
  • 0
    コメント

React×RailsについてLTをしたのでその時のスライド
http://www.slideshare.net/KoheiIto/the-first-react-on-rails-58486821

スライドって声で補う前提なので、スライドだけみてもよくわからないかなと思ってQiitaに書く

このスライドでは、すでにRailsで開発されているサービスにReact.jsを導入する方法を説明した。実際に使っているコンポーネントを元に、実用的な内容にした。

前の記事が中途半端で終わったので
http://qiita.com/khrtz/items/89773651bf3698aae029

React化にあたって考えること

  • react-railsを使う
  • RailsのView上でのSPAは諦める
  • Rubyで出力しているHTMLをJavaScriptで書き換えることだけ考える
  • Railsがわかる
  • ReactRouterを無理に導入して躓くような真似はしない

in progress

僕のReact化計画の途中経過
- スマホサイトだけReact 7割くらいReactで動いている
- ES2015の機能しか使ってない
- Flux使ってない
- ページングとか未実装
- 簡単なstatelessなコンポーネント実装

do

Rails Directory
-app
-bin
-config
-client/react-client/src

ルートディレクトリに/clientを作り、client/react-client/src/component/*.js のようなコンポーネント群を作っていく

client
-src/conponents/.js
-src/containers/
.js
-src/ClientGlobal.js

ClientGlobal.jsでグローバル変数を管理する。gulpではこのファイルをcompileさせる

ClientGlobal.js
import Items from ./comnents/items.js
import CommentPage from './container/CommentPage'

window.Items = Item
window.CommentPage = CommentPage

Browserify,Babelを使用するとgulpはこんな感じ

gulpfile.js
gulp.task('compile-es6', function() {
        var files;
        files = glob.sync('./client/react-client/src/ClientGlobal.js');
      return browserify({entries: files, debug: true})
            .transform('babelify')
            .bundle()
            .pipe(source('bundle.js'))
            .pipe(gulp.dest('app/assets/javascripts/components'));
    });

簡単にReact.Componentを作ってみる。今回はコメントページを実装する

親コンポーネント

CommentPage.js
import React from 'react'
import Comment from '../components/comment.js'

export default class CommentPage extends React.Component {
  render () {

    const commentsNodes = this.props.comments.map(( comment ) => {
      return(<Comment comment={comment} key={comment.id}></Comment>)
    });

    return (
      <div>
      {commentsNodes}
      </div>
    );
  }
}

子コンポーネント

Comment.js
import React from 'react'

export default class Comment extends React.Component {
  render() {
    return(
        <section className="comment Item-cheer">
          <h1 className="u-d_none">コメント</h1>
          <article className="Item__comment Media">
            <div className="Item__comment-img Media__img">
              <img className="Item__comment-img-prop" src={this.props.comment.profile_image} width="20" height="20" alt={this.props.comment.user.name} />
            </div>
            <div className="Item__comment-main Media__body">
              <header className="Item__comment-header u-clrfix">
                <h1 className="Item__comment-name u-d_ib"><b className="u-em">{this.props.comment.user.name}</b>さん</h1>
                <time className="Item__comment-date">{this.props.comment.days}</time>
              </header>
              <div className="Item__comment-body">
                <p>{this.props.comment.body}</p>
              </div>
            </div>
          </article>
        </section>
    );
  }
}

RailsのView react-railsのreact_componentを使ってReactを呼び出す。呼び出したい箇所に置く。同時にjbuilderをrenderしている

comments_smart_phone.html.erb
<%= react_component(CommentPage', render(template: '/projects/comments.json.jbuilder'), {prerender: false}) %>

データをどうやって表示するか?to_json?as_json?では物足りなくて、Reactでヘルパーやメソッド、RubyのViewと同じことができないといけないため、jbuilderを作る。この作業が結構手間だが、まぁ最初だけ。出力するjsonを制限するという意味でも必要。

jbuilderにはpartial!という機能があってテンプレートを作ることができる。jbuilderでよくわからないのが、Arrayを渡す場合、partial呼び出して collectionで配列を渡す。これ以外の方法でうまく動かなかった点。
これはコントローラーの対応するviewの中に作ればいい。

comments.json.jbuilder
json.comments do
   json.partial! partial: 'comments', collection: @items, as: :item
end

jbuilderにもcacheの機能がついている。今回は1時間ごとにキャッシュしている。React使うからキャッシュできないとかいう不安はない。
jbuilderでRubyを好き勝手書けるのでいい。

最初はDraperとか導入してみたり、結構よくわからないことやろうとしてた。Viewにif文とかある場合、それどうすんのとか。全部jbuilderが解決してくれた。

comment.json.jbuilder

json.cache! [item], expires_in: 1.hours do
    json.days item.reserved_at.strftime('%Y年%m月%d日')
    if item.user.is_deleted?
      nil
    else
      json.user_link user_path(item.user)
    end

     json.profile_image asset_url item.user.my_image_url

      json.user item.user, :name

    if item.comment.present?
      json.body item.comment
    else
      json.body "よろしくお願いします。"
    end
end

サーバーサイドレンダリングとか難易度高いのでハマるかもしれない。この環境ではSprocketにのせる前に、gulpでcompileしているので問題ない。app/assets/javascript以下にES6とか書いてる場合、サーバーサイドレンダリングできない。react_railsのサーバーサイドレンダリングではexecjs(v8)を使うんだけどrequire()が使えないため動かない。

前にこんなの書いた
http://qiita.com/khrtz/items/bb8ee0731ce17b997282

app/assets/以下にReactを書いてはいけない。

新規でRailsアプリを立ち上げる場合、こういうやり方はもうしなくなっていくと思う。Rails5ではrails-apiの機能が使えるようになって、SPAがゴリゴリ書けるようになるんじゃないかな。

スライドもみてもらえたら嬉しいです
http://www.slideshare.net/KoheiIto/the-first-react-on-rails-58486821