React入門#4 JSX

これまでのサンプルではrender()内でReact.createElement()React.DOM.*()を使ってUIを定義しました。

render: function() {  
  return React.DOM.div(
    null,
    React.DOM.textarea({
      rows: 5,
      cols: 30,
      value: this.state.text
    }),
    React.DOM.p(
      null,
      React.DOM.small(null, 'Count: ' + this.state.text.length)
    )
  )
}

HTMLのネストに応じてメソッドの呼び出しもネストされるので、既に直感的では無いと感じると思います。そこで、HTMLに近い記述でUIを記述することができるJSXを使うことにします。

上記のrender()は、JSXで以下のように書くことができます。(事前にreact/build/JSXTransformer.jsをロードするのと、<script type="text/jsx"></script>で囲む必要があります。)

render: function() {  
  <div>
    <textarea rows={5} cols={30} value={this.state.text} onChange={this._textChange} />
    <p><small>Count: {this.state.text.length}</small></p>
  </div>
}

JSXTransformer.js<script type="text/jsx">スクリプトをブラウザで表示できるように変換してくれます。JS部分をHTMLから分離する場合にも<script type="text/jsx" src="app.js"></script>のようにします。

Babel

現時点(React v0.13.3)ではJSTransformerがReactに付属して、これを利用していますが、次期バージョンからはBabelが使われるようです。

Babelのインストール

BabelでJSXをJSに変換するようにします。今回はコマンドラインでJSXを記述したファイルの変更を監視し、ビルドするようにします。

% npm install -g babel
...
% banel --version
5.8.21 (babel-core 5.8.22)

% babel src --watch --out-dir build/

ビルドされたファイルは通常のJavaScriptファイルなので<script>type属性は必要ありません。

JSXとHTMLの比較

基本的に、JSXの書き方を忘れたらオンラインのコンパイラで確認しつつ書くことができますが、基本的なことを押さえておきます。

classとfor

classforはJavaScriptの予約語なので、JSX上ではclassNamehtmlForを使います。

<div className="column"></div>  
<label htmlFor="name">Your name: </label>  

インラインスタイル

スタイルはオブジェクトとして渡します。CSSプロパティはcamelCaseで記述します。

var containerStyle = {  
  border: '1px solid #FF0000',
  fontSize: '20px'
};

return (  
  <div style={containerStyle}></div>
);

または、以下のようにしても同じです。

<div style={{border: '1px solid #FF0000', fontSize: '20px'}}>  

閉じタグは必須

HTMLでは閉じタグを忘れても何とかなってしまいますが、JSXでは変換時にエラーとなります。

<hr>  
-> Expected corresponding JSX closing tag for <hr>

<br>  
-> Expected corresponding JSX closing tag for <br>

属性はcamelCase

onclickonClickになります。ただし、data-*aria-*属性は通常のHTMLと同じです。

FORM

JSXでinputのvalueの値を設定すると、入力内容の変更に応じて、常に最新の属性値が得られます。逆にデフォルト値を設定したい場合にはdefautValueを使います。

var TextInput = React.createClass({  
  output: function(ev) {
    console.log('new value: ' + ev.target.value);
    console.log('default value: ' + ev.target.defaultValue);
  },

  render: function() {
    return (
      <div>
        <input type="text" defaultValue="hoge" onChange={this.output} />
        <select defaultValue="1">
          <option value="0">0</option>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>

        <select defaultValue={["2", "3"]} multiple={true}>
          <option value="0">0</option>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
          <option value="4">4</option>
        </select>
      </div>
    );
  }
});

React.render(  
  React.createElement(TextInput),
  document.getElementById('app')
);