React.jsを触ってみた
本家にあるTutorialをさわってみたのでメモ。
serverを動かす
https://github.com/reactjs/react-tutorial/
ここにserverのスクリプトが置いてある。download
POSTメソッドを動かす必要性もあるので、serverを動かす必要があるのですが、serverを起動するスクリプトも入っている。いつもRuby使ってるので、僕はRubyで。
ruby server.rb
http://localhost:3000/ で動いているかどうか確認できる。
index.htmlを準備する
public/index.htmlをつくる。 すでにあるのを削除して作っていく事にした。 これをコピーすれば良いらしい。
<!-- index.html --> <html> <head> <title>Hello React</title> <script src="https://fb.me/react-0.13.1.js"></script> <script src="https://fb.me/JSXTransformer-0.13.1.js"></script> <script src="https://code.jquery.com/jquery-1.10.0.min.js"></script> </head> <body> <div id="content"></div> <script type="text/jsx"> // Your code here </script> </body> </html>
Componentを作ってみる。
JSXな書き方とplainJSな書き方があるらしい。
// JSXな書き方 var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> Hello, world! I am a CommentBox. </div> ); } }); React.render( <CommentBox />, document.getElementById('content') );
// plainJSな書き方 var CommentBox = React.createClass({displayName: 'CommentBox', render: function() { return ( React.createElement('div', {className: "commentBox"}, "Hello, world! I am a CommentBox." ) ); } }); React.render( React.createElement(CommentBox, null), document.getElementById('content') );
JSX in Depthを見る限り「JSXを使わなくてもいいけど、オススメしてるよ」って言ってるので、JSXなスタイルでいこうと思う。
ソースを見る限り、createClassでComponentを作って、JSX部分ではHTMLを作る処理を書き、React.renderで描画するものと場所を指定しているっぽい。
ということは下記だけでも同様な動きになりそう。
React.render( <div className="commentBox"> Hello, world! I am a CommentBox. </div> , document.getElementById('content') );
同様な動きでした。
Componentをネストしてみる。
commentBoxというコンポーネントにh1タグ、commentListのコンポーネント、commentFormのコンポーネントをネストしたもの(下記)を作ってみる。
<div class="commentBox"> <h1>Comments</h1> <div class="commentList"> Hello, world! I am a CommentList. </div> <div class="commentForm"> Hello, world! I am a CommentForm. </div> </div>
実際に組んでみると下記のようになる。
var CommentList = React.createClass({ render: function() { return ( <div className="commentList"> Hello, world! I am a CommentList. </div> ); } }); var CommentForm = React.createClass({ render: function() { return ( <div className="commentForm"> Hello, world! I am a CommentForm. </div> ); } }); var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList /> <CommentForm /> </div> ); } }); React.render( <CommentBox />, document.getElementById('content') );
ちなみにJSXを使わない場合は下記。
var CommentList = React.createClass({displayName: 'CommentList', render: function() { return ( React.createElement('div', {className: 'commentList'}, "Hello, world! I am a CommentList." ) ); } }); var CommentForm = React.createClass({displayName: 'CommentForm', render: function() { return ( React.createElement('div', {className: 'commentForm'}, "Hello, world! I am a CommentForm." ) ); } }); var CommentBox = React.createClass({displayName: 'CommentBox', render: function() { return ( React.createElement('div', {className: "commentBox"}, React.createElement('h1', null , "Comments"), React.createElement(CommentList, null), React.createElement(CommentForm, null) ) ); } }); React.render( React.createElement(CommentBox, null), document.getElementById('content') );
createElementの第3引数以降にelementのインスタンスを入れるとネストされ、並列に置きたいときは第4引数、第5引数としていけばいいらしい。
この点に関しては
<div className="commentBox"> <h1>Comments</h1> <CommentList /> <CommentForm /> </div>
と書く事ができるJSXの方が確かに全然見やすい。
Propsを使ってみる
Propsは親コンポーネントから子コンポーネントへのデータを渡す方法。 とりあえずCommentコンポーネントを作成する。
var Comment = React.createClass({ render: function() { return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> {this.props.children} </div> ); } });
親コンポーネントから子コンポーネントへprops
を通してデータを渡す事ができる。
親コンポーネントのCommentListを下記のように記述する事でCommentコンポーネントはprops.author
の情報を読み取る事ができる。
var CommentList = React.createClass({ render: function() { return ( <div className="commentList"> <Comment author="Pete Hunt">This is one comment</Comment> <Comment author="Jordan Walke">This is *another* comment</Comment> </div> ); } });
またCommentは、ネストされた中身(上の例だとThis is one comment
やThis is *another* comment
の部分)を this.props.children
から取得する事ができる。
jsonデータをCommentListに表示してみる。
まずはデータを用意。
var data = [ {author: "Pete Hunt", text: "This is one comment"}, {author: "Jordan Walke", text: "This is *another* comment"} ];
データをCommentBox -> CommentListという感じで渡していく。
var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.props.data} /> <CommentForm /> </div> ); } }); React.render( <CommentBox data={data} />, document.getElementById('content') );
CommentListでdataを使って表示。
var CommentList = React.createClass({ render: function() { var commentNodes = this.props.data.map(function (comment) { return ( <Comment author={comment.author}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); } });
dataという配列をmap関数で各要素のhtmlを作ってそれを繋げて、{commentNodes}
で表示している。
dataをサーバーから取ってくる。
"http://localhost:3000/comments.js" からコメントを取得して、表示してみる。
ソースはこうなる。
var CommentBox = React.createClass({ loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, getInitialState: function() { return {data: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm /> </div> ); } }); React.render( <CommentBox url="comments.json" pollInterval={2000} />, document.getElementById('content') );
getInitialStateはコンポーネントが作成されて一番始めに呼ばれ、componentDidMountはrender処理が走ったときに呼ばれる。
ここでは getIntialStateで空のdataプロパティを作成し、render時にAPIを走らせてデータを取得したときにsetStateでdataにAPIの結果を詰めている。 またsetIntaervalで2秒毎にAPIを読んでアップデートしている。
setStateはpropsを変更するときに使うメソッドみたい。
コメントを追加してみる。
formを作ってそこからコメントを追加していく。
プログラムの流れとしては
CommentFormでsubmitすると、空チェックして、CommentBoxに値を投げる。そして、成功したら表示、失敗したらそれなりの処理をする。
という感じ。
CommentFormはこういう感じになる。
var CommentForm = React.createClass({ handleSubmit: function(e) { e.preventDefault(); var author = React.findDOMNode(this.refs.author).value.trim(); var text = React.findDOMNode(this.refs.text).value.trim(); if (!text || !author) { return; } this.props.onCommentSubmit({author: author, text: text}); React.findDOMNode(this.refs.author).value = ''; React.findDOMNode(this.refs.text).value = ''; return; }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" ref="author" /> <input type="text" placeholder="Say something..." ref="text" /> <input type="submit" value="Post" /> </form> ); } });
submitされた場合に、handleSubmitが呼ばれるようにして、値は“refs“を使って取得。
下記のように、props.onCommentSubmit
にCommentBoxへのCallbackを登録しておくことで
CommentForm -> CommentBoxというデータの流れを作る事ができる。
var CommentBox = React.createClass({ loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, handleCommentSubmit: function(comment) { $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, getInitialState: function() { return {data: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); } });
一部飛ばしてるけど、以上でチュートリアルは終了。