Reactを書く際に複数のAltJSを混在させてみる

[GitHubへのリンク](https://github.com/wordijp/using-multiple-altjs-for-react)

# 概要

**環境をバージョンアップしたことによりTypeScriptにreact-jadeを埋め込む処理がエラーが出るようになったため、外部ファイルのjadeテンプレートを利用する方法へ変更しています**

これReactを使う際に複数のAltJS(TypeScript & CoffeeScript & Babel)を混在させるサンプルアプリケーションについての記事です。
AltJSを中間ファイルであるjsへと個別に吐きだし、Browserifyでbundleする事により対応しています。

## 各AltJSのReact対応について

※リンク先はgulp用のプラグイン

- [TypeScript](https://www.npmjs.com/package/gulp-typescript)
  - jsx構文を使えるトランスパイラがまだ無い為、[react-jade](https://github.com/jadejs/react-jade)を利用してHTMLタグを記述しています。  
    react-jadeのコードはBrowserifyでbundle時にreact-jadeプラグインによりES5のJSへとtransformされています。
- CoffeeScript
  - jsx構文をサポートしたCoffeeScriptである[coffee-react](https://www.npmjs.com/package/gulp-coffee-react)を利用
- [Babel](https://www.npmjs.com/package/gulp-babel)
  - jsx構文が標準でサポートされています
  - ※[babel6からはパッケージが分離されたため](http://qiita.com/kamijin_fanta/items/e8e5fc750b563152bbcf)、presetsでreactを指定する必要あり、モジュールとしてimport・exportするにはさらにes2015を指定

## 実行結果

![実行結果.png](https://qiita-image-store.s3.amazonaws.com/0/51728/0ecc2dc0-8504-fb70-7dc5-5162596c8441.png)

## コード

メインApp

```coffeescript:app.cjsx
CoffeeReactClass      = require('./coffeereactclass').CoffeeReactClass
CoffeeReactMixinClass = require('./coffeereactmixinclass').CoffeeReactMixinClass
#TSClass               = require('./tsclass').TSClass
TSTmplClass           = require('./tsclass').TSCTmpllass
BabelClass            = require('./babelclass').BabelClass
BabelTmplClass        = require('./babeltmplclass').BabelTmplClass

App = React.createClass(
  render: () ->
    <div>
      <CoffeeReactClass      title="coffee-react" />
      <hr />
      <CoffeeReactMixinClass title="coffee-react" />
      <hr />
      <TSTmplClass           title="TypeScript" />
      <hr />
      <BabelClass            title="Babel" />
      <hr />
      <BabelTmplClass        title="Babel" />
    </div>
)

React.render(
  <App />,
  document.getElementById("app-container")
)
```

メインとなるAppではcoffee-reactを使用していますが、一番短く書けるからってだけで他の言語でも構いません。
ここから各言語で書かれたReactコンポーネントを表示しています、各コンポーネントにはそれぞれタイトルを渡し、各自で表示するように統一しています。
<hr />

coffee-react

```coffeescript:coffeereactclass.cjsx
CoffeeReactClass = React.createClass(
  render: () ->
    items = [
      "item1"
      "item2"
      "item3"
    ]

    <div>
      <h1>{this.props.title}</h1>
      <ul>
        {items.map((x, i) -> <li key={i}>{x}</li>)}
      </ul>
    </div>
)

module.exports.CoffeeReactClass = CoffeeReactClass
```

単に親からタイトルを受け取り、表示しています。
<hr />

mixinを利用したcoffee-react

```coffeescript:coffeereactmixinclass.cjsx
Mixin =
  render: () ->
    <div>
      <h1>{this.state.title}</h1>
      <ul>
        {this.state.items.map((x, i) -> <li key={i}>{x}</li>)}
      </ul>
    </div>


CoffeeReactMixinClass = React.createClass(
  mixins: [Mixin]
  getInitialState: () ->
    title: this.props.title + " (using mixin)"
    items: [
      "hoge"
      "fuga"
      "piyo"
    ]
)

module.exports.CoffeeReactMixinClass = CoffeeReactMixinClass
```

表示部分をMixinオブジェクトとして切り出した方法です。
ReactでViewとControllerが一緒くたになりがちですが、Mixinを利用してViewをMixinオブジェクトへと切り離す試みをしています。

**この記事を書いた後に知った為このままですが、
実際はmixinはES6 class向けにサポートされない為、継承の方が良いです。**

<hr />

~~jadeテンプレートを埋め込んだTypeScript~~
外部ファイルのjadeテンプレートを利用したTypeScript



```jade:test-template.jade
h1= title
ul
  - for (var i = 0; i < items.length; ++i) {
    li(key=i)= items[i]
  - }
```


```ts:tstmplclass.ts
import React = require('react');
var tmpl = require('./test-template');

export var TSTmplClass = React.createClass({
  render: function() {
    var items = [
      "tom",
      "bob",
      "hiroshi"
    ];

    return tmpl({
      title: this.props.title + " (using jade template file)",
      items: items
    });
  }
});
```

jsx構文が書けない為、表示部分をreact-jadeで対応、View部分の切り離しも兼ねています。

~~jadeテンプレートは中間ファイルには残ったままになりますが、Browserifyでbundleする際に~~


~~b.transform('react-jade').bunde()~~


~~としてbundleファイルにJSコードへ変換されて埋め込まれるようにしています。~~
~~react-jadeのライブラリコードが埋め込まれて肥大化する懸念がありましたが、このコードの場合も、外部ファイルにする際もJSコードに変換して埋め込んでしまうので大丈夫でした。~~
**~~Browserifyのバージョンを9.0.7に上げると、変換前のコードとreact-jadeのコードが埋め込まれて肥大化するようになりました、もしかしたら展開するオプションや方法があるかもしれませんが、jadeファイルに分割しておいた方が無難だと思います。~~**
**Node.js v5に上げると、react-jadeでエラーが出るようになったため、外部ファイルのjadeテンプレートを利用する方法へ変更しています。**
ちなみに、TypeScriptでReactの型を扱うには[typed-react](https://www.npmjs.com/package/typed-react)等がありますが、自分の環境だと上手く動かなかった為今回は使用していません。
<hr />

Babel

```js:babelclass.js6(便宜上付けた独自拡張子)
export var BabelClass = React.createClass({

  render: function () {
    var items = [
      "suchi",
      "tempura",
      "fujiyama"
    ]

    return (
      <div>
        <h1>{this.props.title}</h1>
        <ul>
          {items.map(function(x, i) { return <li key={i}>{x}</li>; })}
        </ul>
      </div>
    )
  }
})
```

こちらも単に親からタイトルを受け取り、表示しています。
<hr />

外部ファイルのjadeテンプレートを利用したBabel

```jade:test-template.jade
h1= title
ul
  - for (var i = 0; i < items.length; ++i) {
    li(key=i)= items[i]
  - }
```

```js:babeltmplclass.js6
import tmpl from './test-template'

export var BabelTmplClass = React.createClass({
  render: function () {
    var items = [
      "tendon",
      "katudon",
      "puteranodon"
    ]

    return tmpl({
      title: this.props.title + " (using jade template file)",
      items: items
    });
  }
})
```

テンプレートエンジンであるreact-jadeを利用して、Viewを切り離しています。
新たにjadeを覚える必要がありますが、mixinよりはこちらの方が正攻法だと思います。

## まとめ

基本的に、言語ごとに出来る事に違いはありません、TypeScriptだけreact-jadeの使用を強いられますが(無くても書けるけど、辛い)複数のAltJSを混在させられる為、その都度やりやすい言語を選べば良いと思います。

## 備考

### gulp-react-jadeについて

~~gulpにてjadeテンプレートファイルをjsに変換する際は、[本家のgulp-react-jade](https://www.npmjs.com/package/gulp-react-jade)を利用し、独自にBrowserify用のヘッダ文字列付与を施しています。
個人的に本家版を使いたかったってだけで、~~**[pugjs/react-jade](https://github.com/pugjs/react-jade)を利用した自作のgulpプラグインを利用していますが、**通常は[mizchiさんのmizchi/gulp-react-jade](https://github.com/mizchi/gulp-react-jade)の方を使う方が楽です。


## 参考

- https://github.com/mizchi/gulp-react-jade
- [react-jadeでjadeテンプレートから仮想DOMを出力する](http://qiita.com/mizchi/items/4e17b54cd9cc70d747cc)
- [React.jsのmixinについて](http://qiita.com/koba04/items/55d28f0aba4d1f53e735)
- [React0.13.0-beta.1でのComponentのES6 classでの継承とmixinが使えない理由](http://qiita.com/mizchi/items/ce9a128aa2a47804e083)