読者です 読者をやめる 読者になる 読者になる

WEB+DB PRESS

WEB+DB PRESS vol.86

WEB+DB PRESS Vol.86

WEB+DB PRESS Vol.86

第12回 React によるフロントエンド開発の変革

  • State - コンポーネントの状態管理
  • setState() とコンポーネントの再描画
    • DOM操作についてはついては一切コードを書いて無い
  • Virtual DOM
    • 差分を割り出して DOM の実体を更新する
  • JSX と Virtual DOM
  • React アプリケーションの基本設計
  • Props - 親子コンポーネント間のインターフェイス
  • Props は不変に
    • 子は値を使うだけで、管理するのは親であることを基本原則とする
  • 子から親への処理の委譲
  • react-tools による JSX のビルド
    • jsx ファイルを js ファイルに変換
    • 変換後は普通の js ファイルなので、JSXTransformer はもう必要ない
% jsx -x jsx --watch src/ dest/
  • Browserify + reactify によるビルド
    • Browserify は CommonJS スタイルで分割された JavaScript モジュール群を 依存関係を考慮しながら1つのファイルにまとめてあげてバンドルするツール
    • CommonJS スタイルは module.exports でオブジェクトや関数をエクスポートし require でそれをロードする Node.js でおなじみの仕組み
% npm i -g browserify
% npm i react
% npm i reactify
% ulimit -n 1024
% browserify -t reactify src/app.jsx -o dest/app.js
  • ulimit でファイルディスクリプタの最大を増やさないと(デフォ 256 だった)、browserify コマンドでこける
var React = require('react');

var App = React.createClass({
  getInitialState: function() {
    return {
      message: "",
      savedMessages: []
    };
  },

  updateMessage: function(message) {
    this.setState({ message: message.target.value });
  },

  saveMessage: function(message) {
    console.log(message);
    var messages = this.state.savedMessages.concat(message);
    this.setState({ savedMessages: messages });
  },

  render: function() {
    return (
      <div>
        <MessageInput onChange={ this.updateMessage } onSave={ this.saveMessage } />
        <Message message={ this.state.message } savedMessages={ this.state.savedMessages } />
      </div>
    );
  }
});

var MessageInput = React.createClass({
  _onChange: function(e) {
    this.props.onChange(e);
  },

  _onKeyDown: function(e) {
    if (e.keyCode === 13) {
      this.props.onSave(e.target.value);
      e.target.value = "";
    }
  },

  render: function() {
    return <input type="text" onChange={ this._onChange } onKeyDown={ this._onKeyDown} />;
  }
});

var Message = React.createClass({
  render: function() {
    var key = 0;
    var messages = this.props.savedMessages.map(
      function(message) {
        return <li key={ key++ }>{ message }</li>;
      }
    );

    return (
      <div>
        <p>{ this.props.message }</p>
        <ul>{ messages }</ul>
      </div>
    );
  }
});

React.render(
  <App />,
  document.getElementById('app-container')
);
  • Markdown プレビュー
% npm i markdown
var React = require('react');
var mdparser = require('markdown').markdown;

var App = React.createClass({
  getInitialState: function() {
    return { markdown: "" };
  },

  updateMarkdown: function(markdown) {
    this.setState({ markdown: markdown });
  },

  render: function() {
    return (
      <div>
        <TextInput onChange={ this.updateMarkdown } />
        <Markdown markdown={ this.state.markdown } />
      </div>
    );
  }
});

var TextInput = React.createClass({
  propTypes: {
    onChange: React.PropTypes.func.isRequired
  },

  _onChange: function(e) {
    this.props.onChange(e.target.value);
  },

  render: function() {
    return (
      <textarea onChange={ this._onChange }></textarea>
    );
  }
});

var Markdown = React.createClass({
  propTypes: {
    markdown: React.PropTypes.string.isRequired
  },

  render: function() {
    var html = mdparser.toHTML(this.props.markdown);

    return (
      <div dangerouslySetInnerHTML={{ __html:html }}></div>
    );
  }
});

React.render(
  <App />,
  document.getElementById('app-container')
);

f:id:yossk:20150503120758j:plain

  • PropTypes
    • Props で渡ってくる値の型をチェックしバリデーションを行うための機構
    • HTML 文字列を DOM として追加する
      • dangerouslySetInnerHTML 属性を用いて DOM 追加
  • 落ち葉拾い
    • DOM 操作のために jQuery はもう必要無い
    • Virtual DOM を前提にする以上、生 DOM を直接操作する jQuery を併用できないと考えたほうがよい
    • Flux