修羅の国からえんじにあ

主に Ruby on Rails の実験を書き留めていく予定

RailsのWebpackでReactに挑戦 (3) リスト表示

続きです。・・・ずいぶん時間が経過してますが・・・。

rassy.hatenablog.jp

で、リスト表示ってどうすんの?

さて・・すっかり放置しておりましたが、前回エラーとなっていた部分について、考えてみます。

どうやら、複数のタグをコンポーネントとする場合には、全体を外包する要素が必要な様子。

つまり、これは駄目。

const Hoge = () => (
  <li>hoge</li>
  <li>hoge</li>
  <li>hoge</li>
)

で、これはOK。

const Hoge = () => (
  <ul>
    <li>hoge</li>
    <li>hoge</li>
    <li>hoge</li>
  </ul>
)

という事で、結局これなら動くぞ・・というのがこちら。

index.html.erb

ontpages#index</h1>
<p>Find me in app/views/frontpages/index.html.erb</p>

<section id="section">
  <h2>sample_list</h2>
</section>

<%= javascript_pack_tag 'list_test' %>

list_test.jsx

import React from 'react'
import ReactDOM from 'react-dom'

const Hoge = () => (
  <ul>
    <li>hoge</li>
    <li>hoge</li>
    <li>hoge</li>
  </ul>
)

var target = document.getElementById("section").appendChild(document.createElement("ul"));

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hoge/>,
    target,
  )
})

無事表示です。

f:id:kimny7544:20180603053611p:plain

ただ、これはかなりイケてなくて、レンダリングされる構造は、

<ul>
  <ul>
    <li>hoge</li>
    <li>hoge</li>
    <li>hoge</li>
  </ul>
</ul>

このようになってしまいます。

おかしさ演出で ul にしていますが、これは div だとしても当然狙った構造にはならないです。

やはりコレにしたい。

<ul>
  <li>hoge</li>
  <li>hoge</li>
  <li>hoge</li>
</ul>

ただ、どうやらReactの render の動きとしては、指定した要素の配下にコンポーネントを差し込む為、仕方がないようです。

要は、一旦差し込んでから、別途コンポーネントの親要素のみを削除すると・・・。

なんと非効率的な・・・と思ったらありました。解決方法。

stackoverflow.com

<React.Fragment> というタグ、または <> で囲んでしまえ・・・ということで、やってみました。

結果・・。

const Hoge = () => (
  <React.Feagment>
    <li>hoge</li>
    <li>hoge</li>
    <li>hoge</li>
  </React.Feagment>
)

<React.Feagment> の場合、

f:id:kimny7544:20180603055219p:plain

<> の場合、

f:id:kimny7544:20180603055301p:plain

・・・・・何これ死にたい。

暫くハマった後で気がついたのですが、

github.com

このあたりを眺めていると・・・

あ・・・バージョン・・・。と思い立って調べてみましたが、

07:05:react_test (master *+)$ npm outdated
Package             Current  Wanted  Latest  Location
@rails/webpacker      3.0.2   3.5.3   3.5.3  react_test
coffeescript         1.12.7  1.12.7   2.3.1  react_test
prop-types           15.6.0  15.6.1  15.6.1  react_test
react                16.1.1  16.4.0  16.4.0  react_test
react-dom            16.1.1  16.4.0  16.4.0  react_test
webpack-dev-server    2.9.4  2.11.2   3.1.4  react_test

やはり取り残されていました・・。そういえば、以前このサンプルを作った時からだいぶ経っていました。 しかし、アプデは何か起こりそうなのと、趣旨が変わってしまいそうなので、一旦アプリを作り直します。

package.json

{
  "name": "react_test",
  "private": true,
  "dependencies": {
    "@rails/webpacker": "3.5",
    "babel-preset-react": "^6.24.1",
    "prop-types": "^15.6.1",
    "react": "^16.4.0",
    "react-dom": "^16.4.0"
  },
  "devDependencies": {
    "webpack-dev-server": "2.11.2"
  }
}

ということで、今回は、こんな感じになりました。

では、いざ表示を・・・

f:id:kimny7544:20180605042048p:plain

おおお・・・来ました!!!

ちなみに、

const Hoge = () => (
  <>
    <li>hoge</li>
    <li>hoge</li>
    <li>hoge</li>
  </>
)

↑これは構文エラーのままでした。どうやら、きちんと書かねば駄目っぽいです。

ともあれ、今回はここまでにします。

次はいよいよ動的にリスト表示する段階に行きたいなぁ・・・(ハマらなければ・・・)

RailsのWebpackでReactに挑戦 (2) コンポーネント

rassy.hatenablog.jp

続きです。

前回は、とりあえず起動に成功したので、何を変えたらどうなるのか・・・的な考察をやってみたいと思います。

さて、コンポーネントとはなんぞ?

まずは、Reactを構成するコンポーネントについて考えてみます。

まずは、余分な情報を除きたかったので、サンプルをコピーしつつ、最初の定義らしいものを表示させるようにしてみます。

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const Hello = props => (
  <div>Hello {props.name}!</div>
)

const Hoge = () => (
  <li>hoge</li>
)

Hello.defaultProps = {
  name: 'David'
}

Hello.propTypes = {
  name: PropTypes.string
}

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(document.createElement('div')),
  )
})

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hoge/>,
    document.body.appendChild(document.createElement('ul')),
  )
})

このようにサンプルを書き換えてみました。

追記箇所としては、

const Hoge = () => (
  <li>hoge</li>
)


document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hoge/>,
    document.body.appendChild(document.createElement('ul')),
  )
})

この部分となります。

まずは最初のステップですので、ステータスやバリデーションらしきモノを排除してプレーンな定義としています。

しらべてみると、ここの

const Hoge = () => (
  <li>hoge</li>
)

が、コンポーネントとなり、やっているのは、アロー関数を使った定義のようです。

よく書籍なんかで出ている React.createClass の記法(すでに非推奨)を今風に書き換えたものらしい。

個人的には、

const Hoge = () => {
  return(
    <li>hoge</li>
  )
}

このぐらいでも良いけど、(実際動く)が、折角新しい事を覚えるので、シンプルに覚えたいですね。

さて、結果ですが・・・

f:id:kimny7544:20171128030052p:plain

無事にリスト表示されています。

f:id:kimny7544:20171128030356p:plain

デベロッパーツールでもこの通り。

しかし、ソース表示だと、

f:id:kimny7544:20171128030933p:plain

ファイル呼んでるだけ・・・まぁ、そらそうですよねw

ちなみにですが、

const Hoge = (props) => (
  <li>hoge</li>
)

このようにやってもエラー出ませんし、

const Hoge = (props) => (
  <li>hoge{props.name}</li>
)

こうしてもエラーになりません。

render側で、

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hoge name="hoge" />,
    document.body.appendChild(document.createElement('ul')),
  )
})

このように、 <Hoge/>,name を付け加えると値が反映されます。

f:id:kimny7544:20171128032257p:plain

狙い通りきちんと、fugafuga したようです。

さて、ここで疑問なのですが、

const Hoge = props => (
  <li>hoge{props.fuga}</li>
)

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hoge huga="hoge" />,
    document.body.appendChild(document.createElement('ul')),
  )
})

このように、 namefuga と適当に変えてみるとどうなるのか試したらどうなるか。

f:id:kimny7544:20171128030052p:plain

はい。今度は残念な結果となりました。

これはアローの引数が、何か固定のプロパティ名を複数?持っているという話になりますね。

詳しく調べるのは後回しとしまして・・・次に、 props は通常何でも良いと考えていたのですが、プロパテイをすでに持っているとなるとちょっと怖いので、念のため試しておきましょう。

const Hoge = fugafuga => (
  <li>hoge{fugafuga.name}</li>
)

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hoge name="hoge" />,
    document.body.appendChild(document.createElement('ul')),
  )
})

このように変更すると・・・

f:id:kimny7544:20171128032257p:plain

あ・・・これは大丈夫のようです。

ということはやはり、ただのブロック変数という事ですね。

しかし、他に用途がないのであれが、ネーミング的には props でしっくり来るのでそのままで良い気がしますね。

という事で、最低限書いておくと良い記述としては、

const Hoge = 変数名 => (
  <div>HTMLが普通に書ける{ ブロックで変数のプロパティが展開出来る }</div>
)

こんな感じかと思われます。

さて、ここで気を良くして、「折角リストなんだから複数入れてみよう。」という話になりますよね。

試してみます。

const Hoge = props => (
  <li>hoge</li>
  <li>hoge</li>
  <li>hoge</li>
)

はい・・ではどうなるか・・。

f:id:kimny7544:20171128035352p:plain

結果:惨敗

駄目みたいですね。というかエラー画面が無駄にカッコいいんですけど・・・。

エラー画面で折れたので、今日はここまでです。

次回は、リスト表示などのくりかえし要素を作る方法を実験する予定です。

RailsのWebpackでReactに挑戦 (1)

タイトルそのままなのですが、前回作った環境 を元にReactのチュートリアルをやってみたいと思います。

が、今回はただの準備編です。まだ内容には触れずに導入時に作られたサンプルを確認して概要を掴んでみます。

今回の実行環境です。

$ ruby -v
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin15]

$ bundle exec rails -v
Rails 5.1.4

$ node -v
v8.7.0

実装します。

まずは、トップページのコントローラーを作ります。

react_test $ bundle exec rails g controller frontpages index
Running via Spring preloader in process 4765
      create  app/controllers/frontpages_controller.rb
       route  get 'frontpages/index'
      invoke  erb
      create    app/views/frontpages
      create    app/views/frontpages/index.html.erb
      invoke  test_unit
      create    test/controllers/frontpages_controller_test.rb
      invoke  helper
      create    app/helpers/frontpages_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/frontpages.coffee
      invoke    scss
      create      app/assets/stylesheets/frontpages.scss

トップページを作ります。

routes.rb

Rails.application.routes.draw do
  root 'frontpages#index'
end

Railsでインストールした時に作られたサンプルを確認してみましょう。

app/javascript/packs/hello_react.jsx (それにしても階層にすっごい違和感w)

// Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file,
// like app/views/layouts/application.html.erb. All it does is render <div>Hello React</div> at the bottom
// of the page.

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const Hello = props => (
  <div>Hello {props.name}!</div>
)

Hello.defaultProps = {
  name: 'David'
}

Hello.propTypes = {
  name: PropTypes.string
}

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(document.createElement('div')),
  )
})

むぅ・・・なんだコレ・・・。

JSもままならぬ身としてはキツイサンプルです。

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

これはライブラリを呼んでいるっぽい。どうやらライブラリ自体はローカルにインストールされていますね。

それぞれ・・・

node_modules/react/index.js
node_modules/react-dom/index.js
node_modules/prop-types/index.js

ここにいるようです。 そして・・・

const Hello = props => (
  <div>Hello {props.name}!</div>
)

これは・・・アロー関数みたいなのだけれども・・・中身にHTMLが・・・?? そして、引数付きメソッドのような感じ??

でもconst なので、定数ですよね・・。引数付き定数??

おまけに、次の

Hello.defaultProps = {
  name: 'David'
}

Hello.propTypes = {
  name: PropTypes.string
}

に至ってはだいぶ難解ですが、どうもこちらはバリデーションか何かっぽいですね。 これは要実験です。

私のようなボンクラがこれ以上考えても仕方ないので、とりあえず動かしてみましょう。

ともかくサンプルを呼び出す記述が書いてあったので言うとおりに・・・。

app/views/frontpages/index.html.erb

<h1>Frontpages#index</h1>
<p>Find me in app/views/frontpages/index.html.erb</p>

<%= javascript_pack_tag 'hello_react' %>

と、 <%= javascript_pack_tag 'hello_react' %> を追記してみます。

起動します。

1つ目のターミナルではRails

bundle exec rails s

2つ目のターミナルでは、webpackを動かします。

./bin/webpack-dev-server

トップページ、localhost:3000に接続すると・・・

f:id:kimny7544:20171122025706p:plain

よし・・・ここまで壮大な手間をかけてようやくHelloWorld的なやつができました。

ここまでやって今更なのですが、ちょっとカッコよくreact言ってみたかっただけなのに何なの?この苦行・・・。

そして起動だけで駄文が長くなったので、サンプルの実験は一旦切る事を決意w

Rails で react を動かす

Rails で react を動かしてみようと思った話

reactとか正直ミリも知らないのに試してみた。

Rails5.1から導入されたwebpackを使ってみた事から始まり、どうせならReactを試してみよう・・・となってしまった流れ。

前提

  • Node.jsがインストールされている (試した環境では v6.11.4)
  • Railsが動かせる環境がある (試した環境では ruby -v 2.4.0)
  • Macローカル環境でbrewが使える (環境依存のものは読み替えの必要あり)

適当にプロジェクトを作る

$ mkdir react_test
$ cd react_test
$ bundle init

webpackを使いたいので、 Rails 5.1系の最新版の記述を、Gemfileに追記

Gemfile


gem 'rails', '~>5.1.0'

プロジェクト内、vendor/bundle配下にgemを展開

$ bundle install --path vendor/bundle

確認


$ yarn --version


yarnが入っていない場合はインストールする

$ brew install yarn

プロジェクトフォルダにnewする

$ bundle exec rails new . --webpack=react

何かで手順が狂った場合はwebpacker単独でインストールの続行

$ bundle exec rails webpacker:install:react

webpack用のポートを書き換え(8080番に)

別途サーバーを起動する必要があるそうな。

config/webpacker.yml

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost
    port: 8080
    public: localhost:8080
    hmr: false
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    disable_host_check: true
    use_local_ip: false

起動

$ bundle exec rails s
$ ./bin/webpack-dev-server

以下は続きの小ネタ

サンプルとして設置されているjsxを呼び出す

(ここまでは試しておきたい)

$ bundle exec rails g controller toppages index


config/routes.rb

root 'toppages#index'

app/views/toppages/index.html.erb

<%= javascript_pack_tag 'hello_react' %>

Material-UIを使う準備をする

(使うとは言ってない)

$ yarn add redux
$ yarn add react-redux
$ yarn add redux-devtools --dev
$ yarn add material-ui
$ yarn add react-tap-event-plugin

起動を楽にする

(ログが混濁するので、使わなかった)

gem 'forman'

gemを追加後カレント以下にProcfileを作成

Procfile


rails: bundle exec rails s -p 3000
webpack: bin/webpack-dev-server

$ foreman start

heroku で poltergeist 動かしたかったので

Phantom.js の開発が止まってしまった・・・。

とは、関係なくちょいとPhantom.jsを使ってHerokuで poltergeist & capybara を動かしたかったので、

Heroku で Phantom.jsとRailsを共存させた環境を作る状況を調べてみました。


buildpackを入れ直す

$ heroku buildpacks:clear 

$ heroku buildpacks:add https://github.com/stomita/heroku-buildpack-phantomjs

$ heroku buildpacks:add heroku/ruby

確認

$ heroku buildpacks

最後に heroku/ruby が来ていればOK(今回は2個中の2個目)

Buildpack added. Next release on APP-NAME will use:
  1. https://github.com/stomita/heroku-buildpack-phantomjs.git
  2. heroku/ruby
Run git push heroku master to create a new release using these buildpacks.

Herokuがどんどん便利になっているのですが、情報が変わるのが早くて良くハマります。

参考url

https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app

http://logic.moo.jp/data/archives/1060.html

https://github.com/stomita/heroku-buildpack-phantomjs

http://engineer.gahara.me/entry/heroku-ruby-phantomjs

はじめました

主に、書きなぐりや備忘録程度で Ruby on Rails に関して書いていこうと思います。

内容は未定です。

  • テンプレートエンジンは使わない

  • なるべく解説付き

  • なるべく頑張る

のスタイルでゆるーく書いていければと思っております。