ReactのTutorialをArdaで写経してみる(Babel版)

※ 3/18 arda 0.13向けにコードを修正

# コーヒーはあまり好きでは…
[ReactのTutorialをArdaで写経してみる](http://qiita.com/akiramei/items/c4a0397c3aadd81f47aa)
↑前々回の記事でCoffeeScriptを使ったけど、クライアント側の開発でJavaScriptを使いつつ、更にCoffeeScriptを使うのはちょっと…な人も多いか思いBabel(ES6)で書き直してみた。個人的にはCoffeeScriptでサクサク書くのも悪くはないと思うんだけどね。

# [Babel](https://babeljs.io/)ってなに？
![Babel](https://raw.githubusercontent.com/babel/logo/master/logo.png)
なんかPC88ゲームのロゴみたいだ。

>ECMAScript 6のコードを、ECMAScript 5に変換するJavaScriptトランスパイラ

以前は6to5という名前だったけど最近変わったらしい。これを使えばES6の文法を使いつつ、現状の環境で動作させることができる。やっぱ**=>**とか**class**は使いたいからね。

# 環境設定

gulpを使うので出番はないけど、BabelのCLIをインストール。

```
> npm install -g babel
```

開発言語をCoffeeScriptからES6(Babel)へ変更するのでgulpfile.jsを修正する。

```js
var gulp = require('gulp');
var babelify = require('babelify');
var browserify = require('browserify');
var source = require('vinyl-source-stream');

gulp.task('build', function() { 
    return browserify({ 
        entries:['./src/App.jsx'],
        extensions:['js', 'jsx']
    })
    .transform(babelify)
    .bundle() 
    .pipe(source('bundle.js')) 
    .pipe(gulp.dest('./public'));
});

gulp.task('default', ['build']);
```

変更箇所をみてみよう。

```js
    return browserify({ 
        entries:['./src/App.jsx'],
        extensions:['js', 'jsx']
    })
```

拡張子が.jsxになっているね。言い忘れてたけど、BabelはJSXを解釈できるのでコード中に埋め込みまくった。

```js
    .transform(babelify)
```

Babel(ES6)->ES5変換にbabelifyを使う。

```
> npm install babelify babel --save-dev
```

忘れずインストール。(ついでにbabelも)

準備はこんなところ。

# Babelでチュートリアル開始
やってることは前回と同じなので、ぱっぱとコードを貼っていこう。

```js
// Comment.jsx
import {mixin} from 'arda';
import md2react from 'md2react';

var Comment = React.createClass({
    mixins: [mixin],
    render() {
        return (
            <div className='comment'>
                <h2 className='commentAuthor'> 
                    {this.props.author} 
                </h2>
                { md2react(this.props.children.toString()) }
            </div>
        );
    }
});

module.exports = Comment;
```

arda 0.13ではコンポーネントの作成にReact.createClassを使うようになった。ES6のクラスは使わない。~~解説もバッサリカット~~

React.createElementだった箇所はJSXになってる。React本家のチュートリアルを見ていれば、こっちの方が馴染んているかも？

```js
// CommentList.jsx
import {mixin} from 'arda';
import Comment from './Comment.jsx';

var CommentList = React.createClass({
    mixins: [mixin],

    render() {
        let commentNodes = this.props.data.map((comment) => (
                    <Comment author={comment.author}>
                        {comment.text}
                    </Comment>
                )
        ); 

        return (
                <div className='commentList'> 
                    {commentNodes} 
                </div>
        );
    }
});

module.exports = CommentList;
```

解説が必要なところはないかな。

```js
/// CommentForm.jsx
import {mixin} from 'arda';

var CommentForm = React.createClass({
    mixins: [mixin],
    render() {
        return (
                <form className='commentForm' onSubmit={this.handleSubmit.bind(this)}>
                    <input type='text' placeholder='Your name' ref='author'/>
                    <input type='text' placeholder='Say something...' ref='text'/>
                    <input type='submit' value='Post'/>
               </form>
        );
    },

    handleSubmit(e) {
        e.preventDefault();
        let author = React.findDOMNode(this.refs.author).value.trim();
        let text = React.findDOMNode(this.refs.text).value.trim();
        if (!author || !text)
            return;

        this.dispatch('commentSubmit', {author, text});

        React.findDOMNode(this.refs.author).value = '';
        React.findDOMNode(this.refs.text).value = '';
    }

});

module.exports = CommentForm;
```

これもあまり変わらず。ただ、ここに注目。

``` js
{author, text}
```

ES6では、下記のように書かなくてもよくなった。

```js
{
    author: author,
    text: text
}
```

```js
// CommentBox.jsx
import {mixin} from 'arda';
import CommentList from './CommentList.jsx';
import CommentForm from './CommentForm.jsx';

var CommentBox = React.createClass({
    mixins: [mixin],
    render() {
        return (
            <div className='commentBox'>
                <h1>Comments</h1>
                <CommentList data={this.props.data} />
                <CommentForm />
            </div>
      );
    }

});

module.exports = CommentBox;
```

説明はいらないかな。

```js
// App.jsx
window.React = require('react');
window.Promise = require('bluebird');
window.ReactBootstrap = require('react-bootstrap');
import {Context, Router, DefaultLayout, mixin} from 'arda';
import CommentBox from './CommentBox.jsx';
import request from 'superagent';

var App = React.createClass({
    mixins: [mixin],
    render() {
        return (
                <div>
                    <CommentBox data={ this.props.data } />
                </div>
        );
    }
});

class AppContext extends Context {
    get component() {
        return App;
    }

    initState() { return { data:[] }; }

    expandComponentProps(props, state) { return { data: state.data}; }

    loadCommentsFromServer() {
        request
            .get(this.props.url)
            .set({Accept:'application/json'})
            .end((res)=> this.update( ()=> { return {data:res.body}; }) );
    }

    delegate(subscribe) {
        super.delegate();

        subscribe('context:started', ()=> {
            this.loadCommentsFromServer();
            setInterval(()=>this.loadCommentsFromServer(), this.props.pollInterval);
        });

        subscribe('commentSubmit', (comment) => {
            request
                .post(this.props.url)
                .send(comment)
                .set({Accept:'application/json'})
                .end((err, res) => { /* 最新のsuperagentでは第一引数にerrが必要 */
                    if (res.ok) {
                        this.update( (s)=> { 
                            return {data:s.data.concat([res.body])};
                        });
                    } else {
                        console.log(res.text);
                    }
                });
        });
    }
}

window.addEventListener('DOMContentLoaded', ()=> {
    let router = new Router(DefaultLayout, document.body);
    router.pushContext(AppContext, {url:'/comments.json', pollInterval:2000});
});
```

コンテキストはclassを使えるのでContextから継承。

これで完成。

```
> gulp
```

を叩いて、json-serverを起動すれば動くはず。

# まとめ
クライアント側のコードをサクサク書きたいけど、そのために別の言語を使うのはちょっと…なら、Babelを使ってみるのもありかも。

TypeScriptを使ってもよいとは思うんだけど、画面回りは型よりも手軽さの方が嬉しいかなっと。