※ 3/18 arda 0.13向けにコードを修正
コーヒーはあまり好きでは…
ReactのTutorialをArdaで写経してみる
↑前々回の記事でCoffeeScriptを使ったけど、クライアント側の開発でJavaScriptを使いつつ、更にCoffeeScriptを使うのはちょっと…な人も多いか思いBabel(ES6)で書き直してみた。個人的にはCoffeeScriptでサクサク書くのも悪くはないと思うんだけどね。
Babelってなに?
ECMAScript 6のコードを、ECMAScript 5に変換するJavaScriptトランスパイラ
以前は6to5という名前だったけど最近変わったらしい。これを使えばES6の文法を使いつつ、現状の環境で動作させることができる。やっぱ**=>とかclass**は使いたいからね。
環境設定
gulpを使うので出番はないけど、BabelのCLIをインストール。
> npm install -g babel
開発言語をCoffeeScriptからES6(Babel)へ変更するのでgulpfile.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']);
変更箇所をみてみよう。
return browserify({
entries:['./src/App.jsx'],
extensions:['js', 'jsx']
})
拡張子が.jsxになっているね。言い忘れてたけど、BabelはJSXを解釈できるのでコード中に埋め込みまくった。
.transform(babelify)
Babel(ES6)->ES5変換にbabelifyを使う。
> npm install babelify babel --save-dev
忘れずインストール。(ついでにbabelも)
準備はこんなところ。
Babelでチュートリアル開始
やってることは前回と同じなので、ぱっぱとコードを貼っていこう。
// 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本家のチュートリアルを見ていれば、こっちの方が馴染んているかも?
// 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;
解説が必要なところはないかな。
/// 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;
これもあまり変わらず。ただ、ここに注目。
{author, text}
ES6では、下記のように書かなくてもよくなった。
{
author: author,
text: text
}
// 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;
説明はいらないかな。
// 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を使ってもよいとは思うんだけど、画面回りは型よりも手軽さの方が嬉しいかなっと。