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

More than 5 years have passed since last update.

@tektoh

Rails エンジニア向け はじめての React.js 勉強会

This is ...

はじめての React.js

Virtual DOM と JSX

  • ReactはjQueryのようにHTMLからDOMを組み立てません。
  • 最初からJavaScriptのオブジェクトでDOMの構造を組み立て管理しています。
  • JavaScriptで組み立てられた DOM(Virtual DOM)だと人間が読むのにツライので、JSXというHTMLをJavaScriptオブジェクトにコンパイルする仕組みが用意されています。
jQuery
$('#id').append('<div>Seconds Elapsed' + this.state.secondsElapsed + '</div>');
JSで書いたReact
// Objectで仮想DOMを作る。
return React.createElement(
  "div", null, "Seconds Elapsed: ", this.state.secondsElapsed
);
JSXで書いたReact
// JSX
return (
  <div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
  • JSXの中に { } でHTMLの中に動的なデータやJavacriptを挿入することができます。(ERBの<% %>、PHPの<?php ?>とだいたい同じです。JavaScriptでERB書いてる感じです)

コンポーネント

  • 幾つかの HTML で組み立てられた作られたパーツです。
  • Bootstrap の Compornents のようなものです。 スクリーンショット 2015-11-12 19.38.02.png
  • Reactはコンポーネントごとにclassを作ります。classの中にロジックとテンプレートを分離せずに同居した構成になっています。

実例

HajimeteNoReact.png

  • コンポーネントは入れ子にできます。コンポーネントを上手に分けることができればとてもすっきりして見やすい。
  • 同じ階層のコンポーネント間でデータをやり取りするときは親コンポーネントを作って中継します。

コンポーネントの要素

  • React のコンポーネントの要素について軽く触れます。

render()

  • コンポーネントに必ず定義しないといけない関数。
  • 仮想DOMをreturnします。

props

  • コンポーネントに外部から与えられたデータ
  • コンポーネントの中で変更してはいけない
{/* HogeComponent 内で `this.props.foo` => "bar" という感じに取り出せる */}
<div>
  <HogeComponent foo="bar" />
</div>

state

  • コンポーネント内部で管理するデータ
  • コンポーネントの外から変更してはいけない
  • 変更するときは setState() メソッドを使う。
    • setState() で state を変更すると、変更の差分が再描画される。

key

  • 同じコンポーネントをリストで繰り返し配置する場合、Reactが要素の追加・削除・移動などを検出するために、ユニークになるデータを与えてあげる必要があります。
  • key は設定しなくても動作はしますが、key を設定してあげることでReactがDOMの再描画を効率良く行ってくれるようになるのでパフォーマンスが上がります。

RailsでReactするための設定

Gemfile
gem 'react-rails'
  • generator を使うといい感じに環境ができる。
sh
rails g react:install
  • コンポーネントも generator で作れる。
sh
rails g react:component [コンポーネント]
# => app/assets/javascripts/components/[コンポーネント].js.jsx ができあがる
  • ヘルパーでコンポーネントをビューに設置する
erb
<%= react_component('[コンポーネント]') %>
  • JSXはES6シンタックスが使えます。アロー関数 () => {} が便利です。
  • 拡張子を .js.jsx.coffee にすると CoffeeScript でJSXを書くこともできます。

JSX の注意点

  • JavaScriptの予約語とかぶってるアトリビュートがリネームされている
    • class => className
    • for => htmlFor
  • 閉じタグがない場合は /> でタグを閉じる。XHTMLっぽい。
    • <br />
  • jQueryなどで外部からReactでレンダリングしたDOMをいじるとうまく動かない時があります。
    • 使う箇所の住み分けができていれば jQuery と一緒に使っても大丈夫。Bootstrapも使えます。
  • エラーログが丁寧で解りやすいので、デベロッパーツールのコンソールみましょう。

React.js ハンズオン

環境

準備

$ git clone git@github.com:startup-technology/hajimete-no-react.git
$ cd hajimete-no-react
$ bundle install
  • react-rails を追加
Gemfile
gem 'react-rails', '~> 1.5.0'
$ bundle install
  • react の環境設定
$ rails g react:install
      create  app/assets/javascripts/components
      create  app/assets/javascripts/components/.gitkeep
      insert  app/assets/javascripts/application.js
      insert  app/assets/javascripts/application.js
      insert  app/assets/javascripts/application.js
      create  app/assets/javascripts/components.js

コンポーネントを作る

$ rails g react:component Hello
      create  app/assets/javascripts/components/hello.js.jsx
  • このようなファイルができている
app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({

  render: function() {
    return <div />;
  }
});

コントローラーとビューを作成

$ rails g controller hello
  • HelloController に index アクションを追加
app/controllers/hello_controller.rb
class HelloController < ApplicationController
  def index
  end
end
  • routes を設定
config/routes.rb
root 'hello#index'
  • ビューにReactコンポーネントを追加します
app/views/hello/index.html.erb
<%= react_component 'Hello' %>

確認してみる

  • コンポーネントを修正します
app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({

  render: function() {
    return (
      <div>
        <h1>Hello React!</h1>
      </div>
    );
  }
});
  • railsを起動して確認してみましょう
$ rails s

スクリーンショット 2015-12-01 12.59.25.png

リストを作ってみる

  • HelloList というコンポーネントを作って並べてみます。
$ rails g react:component HelloList
  • HelloList は、親コンポーネントから hello という props
app/assets/javascripts/components/hello_list.js.jsx
var HelloList = React.createClass({

  render: function() {
    return (
      <li>Hello {this.props.hello}</li>
    );
  }
});
  • Hello コンポーネントも少し直します。
  • Hello コンポーネントの中に HelloList コンポーネントを設置してみます。
app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({

  render: function() {
    return (
      <div>
        <h1>Hello React!</h1>
        <ul>
          <HelloList hello="hoge" />
          <HelloList hello="fuga" />
        </ul>
      </div>
    );
  }
});
  • 確認してみましょう

スクリーンショット 2015-12-01 13.54.33.png

リストを動的に作ってみる

  • Hello コンポーネントをさらに直します
  • getInitialState で、state の初期値を設定します。
  • map を使い、配列でエレメントを作成しています。
  • エレメントを一旦変数に格納してから埋め込むことができます。
app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({
  getInitialState: function() {
    return {
      hello: ['hoge', 'fuga']
    }
  },

  render: function() {
    var helloList = this.state.hello.map(hello => {
      return <HelloList hello={hello} />;
    });

    return (
      <div>
        <h1>Hello React!</h1>
        <ul>
          {helloList}
        </ul>
      </div>
    );
  }
});
  • 先ほどと同じ内容が表示されていると思います。
    スクリーンショット 2015-12-01 13.54.33.png

  • この時、コンソールを見ると Warning が発生しています。

スクリーンショット 2015-12-01 14.02.51.png

  • Reactに配列で要素を設置する場合はユニークなkeyを設定してあげる必要があります。
  • HelloListにユニークなkeyを渡すように修正してみましょう。
app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({
  getInitialState: function() {
    return {
      hello: [
        {id: 1, text: 'hoge'},
        {id: 2, text: 'fuga'}
      ]
    }
  },

  render: function() {
    var helloList = this.state.hello.map(hello => {
      return <HelloList key={hello.id} hello={hello.text} />;
    });

    return (
      <div>
        <h1>Hello React!</h1>
        <ul>
          {helloList}
        </ul>
      </div>
    );
  }
});
  • これでワーニングが消えたはずです。

フォームを作ってみる

  • HelloForm コンポーネントを作ります。
$ rails g react:component HelloForm
  • JSXでもHTMLと同様にonChangeなどのイベントハンドラを設定することができます。
    • イベントハンドラの設定は onChange={this.foge} のように関数自体を渡していることに注意してください。
    • HTML と同じ感覚で onChange={this.foge()} のように書いてしまうと、render の時に関数が実行されてしまいます。(ハンドラの中で setState していると無限ループになります)
app/assets/javascripts/components/hello_form.js.jsx
var HelloForm = React.createClass({
  getInitialState: function() {
    return {
      value: ''
    }
  },

  handleChange: function(e) {
    this.props.onChangeForm(e.target.value);
  },

  render: function() {
    return (
      <input type="text" onChange={this.handleChange} />
    );
  }
});
  • props に関数を渡すと、子コンポーネントから親コンポーネントに引数でデータを渡すことができるようになります。
app/assets/javascripts/components/hello.js.jsx
var Hello = React.createClass({
  getInitialState: function() {
    return {
      hello: [
        {id: 1, text: 'hoge'},
        {id: 2, text: 'fuga'}
      ],
      value: 'React!'
    }
  },

  handleChangeForm: function(value) {
    this.setState({ value: value });
  },

  render: function() {
    var helloList = this.state.hello.map(hello => {
      return <HelloList key={hello.id} hello={hello.text} />;
    });

    return (
      <div>
        <h1>Hello {this.state.value}!</h1>
        <HelloForm onChangeForm={this.handleChangeForm} />
        <ul>
          {helloList}
        </ul>
      </div>
    );
  }
});
  • このようになります。

ae1353e9b9da3a0253623099ce801b1f.gif

まとめ

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