React.jsをもくもくと触り始めた
とりあえず触ってみないとわからないのでもくもくとチュートリアルをなぞっていく。
A JavaScript library for building user interfaces | React https://facebook.github.io/react/
Starter Kit 0.13.1でさわる
wget https://facebook.github.io/react/downloads/react-0.13.1.zip
unzip react-0.13.1.zip
Getting Started | React をひたすらなぞる
https://facebook.github.io/react/docs/getting-started.html
これをなぞっていく サンプルが下のとおり
<!DOCTYPE html> <html> <head> <script src="build/react.js"></script> <script src="build/JSXTransformer.js"></script> </head> <body> <div id="example"></div> <script type="text/jsx"> React.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script> </body> </html>
text/jsxとなっているのがJSXと呼ばれているXML
JSX in Depth | React https://facebook.github.io/react/docs/jsx-in-depth.html
Reactを使う場合に普通のplainなJSをつかってもいいらしい
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.body);
このようにJSの構文の中にXMLでの記述ができる
これをそのまま React.render
でオブジェクトをつくり、レンダリングできる。
ひとまず簡単にReactを利用したコードを書いて最初のhelloworld.htmlに食わせる。 src/helloworld.js
として保存する。
React.render( <h1>Hello, world!</h1>, document.getElementById('example') );
buildのためにツールを入れる
-> % npm install -g react-tools
/usr/local/bin/jsx -> /usr/local/lib/node_modules/react-tools/bin/jsx
react-tools@0.13.1 /usr/local/lib/node_modules/react-tools
├── jstransform@10.1.0 (base62@0.1.1, source-map@0.1.31, esprima-fb@13001.1001.0-dev-harmony-fb)
└── commoner@0.10.1 (private@0.1.6, commander@2.5.1, graceful-fs@3.0.6, install@0.1.8, q@1.1.2, mkdirp@0.5.0, iconv-lite@0.4.7, recast@0.9.18, glob@4.2.2)
これで jsx
コマンドがつかえるようになる。buildをしてみる。
jsx --watch src/ build/
すると src/
にさっき配置した helloworld.js
がビルドされる。同名のファイルが build/
に配置される。
-> % cat build/helloworld.js
React.render(
React.createElement("h1", null, "Hello, world!"),
document.getElementById('example')
);
また、これに応じてhtmlを書き換える。
<!DOCTYPE html> <html> <head> <script src="build/react.js"></script> </head> <body> <div id="example"></div> <script src="build/helloworld.js"></script> </body> </html>
これでHelloWorldが無事表示される。さきほどHelloWorldのDOMを貼り付けていたところに、scriptを貼り付けていることがわかる。ちなみに、下のようにheadに貼っても表示されない。
<!DOCTYPE html> <html> <head> <script src="build/react.js"></script> <script src="build/helloworld.js"></script> </head> <body> <div id="example"></div> </body> </html>
Tutorial | React をなぞる
次はtutorialを読めと書いてあったのでなぞる https://facebook.github.io/react/docs/tutorial.html
まずこれを型として、text/jsxのところに演習内容をいれていく
<!-- 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>
まずコンポーネントをつくってみる
var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> Hello, world! I am a CommentBox. </div> ); } }); React.render( <CommentBox />, document.getElementById('content') );
レンダリングされ、divができている。ちなみにレンダリングされているDOMをみてみると以下のようになっている。 data-reactid
というプロパティがある。これでreactが生成したDOMを管理しているのだろうなと想像。なんで0
じゃなくて.0
なのかはしらない。
<div id="content"> <div class="commentBox" data-reactid=".0"> Hello, world! I am a CommentBox. </div> </div>
次にこれをplainなJavaScriptでやる方法も書いてる、が面倒なので飛ばす。JSXで書いたほうがDOMつくるのはシンプルに書けるっぽさ。ああでもコメントに書いてあるけど、Reactでの div
は実際のDOMのノードとは違うらしい。なんのことだろ。
次にcomponentをつくることを学んでいく。ちなみに下のコードのままでは書きかけなので動作しない。
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> ); } }); // CommentListとCommentFormを含んでいる var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList /> <CommentForm /> </div> ); } }); // propsでpropertyを取得できるらしい // props = properties だと思われる // こうすることで、`Comment`で`CommentList`からのデータを読むことができる // this.props.childrenでnestした要素をとれるらしい var Comment = React.createClass({ render: function() { return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> {this.props.children} </div> ); } });
次にpropsを利用していく。
// CommentListのなかでCommentを利用している。かつ、propとしてauthorを設定している。 var CommentList = React.createClass({ render: function(){ return ( <div className="commentList"> <Comment author="Pepe Hunt">This is one comment</Comment> <Comment author="Jordan Walke">This is *another* comment</Comment> </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> ); } }); // これはmarkdownへの変換器 var converter = new Showdown.converter(); // `{}` のなかでは普通にJavaScriptのmethodが使えている // 普通にconvertしたHTMLを表示しようとしても<em>が文字列として出力されてしまう // なので、ReactによるXSS対策を無効にするために、innerHTMLをつかうようにしている var Comment = React.createClass({ render: function() { var rawMarkup = converter.makeHtml(this.props.children.toString()); return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> <span dangerouslySetInnerHTML={{__html: rawMarkup}} /> </div> ); } });
markdownの表示のところはイレギュラーかも知れないが、Reactのレンダリング方針がかいま見える。
次に外部からデータを当て込んでいく。
var data = [ {author: "Pepe Hunt", text: "This is one comment"}, {author: "Jordan Walke", text: "This is *another* comment"} ]; 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> ) } }); // CommentListに対してdataのプロパティを渡すことができるのか 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') );
こうすると data
をrender時にCommentBoxに渡すことができる。data
をオブジェクトとして渡していたのを外部から渡すには以下のようにする。
React.render( <Commentbox url="comments.json" />, document.getElementById('content') );
comments.jsonは以下のとおり。
[ {"author": "Pepe Hunt", "text": "This is one comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"} ]
で、apiとして返せば連携できるようになるはず。
// getInitialStateでprops.dataを初期化している var CommentBox = React.createClass({ getInitialState: function() { return {data: []}; }, // ここは単純にserver sideリクエスト // ひさびさにjQueryみた 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) }); }, // componentがレンダリングされると呼ばれる componentDitMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, // this.state.dataで参照できる render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm /> </div> ); } }); // sleepのためのintervalもわたしてる。 // 2000をintでわたしたいから{}にしてるのか React.render( <Commentbox url="comments.json" pollInterval={2000} />, document.getElementById('content') );
これでサーバサイドに問い合わせて、componentにデータを引き渡しレンダリングするところまでできたことになる。
次にコメントを投稿できるようにする。
var CommentForm = React.createClass({ handleSubmit: function(e) { e.preventDefault(); var author = React.findDOMNode(this.refs.author).value.trim(); var author = React.findDOMNode(this.refs.text).value.trim(); if (!text || !author) { return; } React.findDOMNode(this.refs.author).value = ''; React.findDOMNode(this.refs.text).value = ''; return; }, render: function(){ // refのプロパティをつけておくと、上でReact.findDOMNodeでDOMを参照できる 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> ); } });
onSubmitのイベントをhandleSubmit
で受け取っている。実際の送信処理を下のように書く。
var CommentForm = React.createClass({ handleSubmit: function(e) { e.preventDefault(); var author = React.findDOMNode(this.refs.author).value.trim(); var author = 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(){ // refのプロパティをつけておくと、上でReact.findDOMNodeでDOMを参照できる 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> ); } }); var CommentBox = React.createClass({ getInitialState: function() { return {data: []}; }, 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) { // コメントが返ってくるに空でも表示しておく var comments = this.state.data; var newComments = comments.concat([comment]); this.setState({data: newComments}); $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function(data) { this.setState({data: data}) }, error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, componentDitMount: 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> ); } });
これでひと通りコメントの追加、表示などができるようになったことになる。