Edited at

React.jsチュートリアルをやる(3)

More than 3 years have passed since last update.

React.jsチュートリアルをやる(2)の続き。


コメントの追加


フォームをつくるコメント

コメントを送信するためのコメントフォームを作る

var CommentForm = React.createClass({

handleSubmit: function(e) {
e.preventDefault();
var author = ReactDOM.findDOMNode(this.refs.author).value.trim();
var text = ReactDOM.findDOMNode(this.refs.text).value.trim();
if (!text || !author) {
return;
}
// TODO: サーバにリクエストを送信
ReactDOM.findDOMNode(this.refs.author).value = '';
ReactDOM.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>
);
}
});


イベント

コンポーネントにイベントを張る場合はキャメルケースで書く。上のコードではフォームにonSubmitイベントを登録していて、フォームがsubmitされるとhandleSubmit()が呼ばれる。


Refs

コンポーネントにref属性をつけることで、コンポーネントを識別できるようなる。上のコードではhandleSubmitの中のthis.refs.authorthis.refs.textでそれぞれのインプットボックスを取得している。

ReactDOM.findDOMNode(component)で実際のDOM要素を取得できる。


子から親へ

コメント投稿フォームでコメントがsubmitされた場合、CommentListコンポーネントのデータをリフレッシュする必要がある。

コメントリストのstateはCommentBoxで管理しているので、必要なロジックはCommentBoxに書く。

<CommentForm onCommentSubmit={this.handleCommentSubmit} />onCommentSubmithandleCommentSubmitを結びつけることで、onCommentSubmitが呼ばれるたびにhandleCommentSubmitが実行されるようになる。

var CommentBox = React.createClass({

loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
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) {
// TODO: サーバに送信、リストをリフレッシュ
},
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>
);
}
});

コールバックの準備ができたので、コメントがsubmitされたらCommentFormからコールバックを呼べるようにする。

var CommentForm = React.createClass({

handleSubmit: function(e) {
e.preventDefault();
var author = ReactDOM.findDOMNode(this.refs.author).value.trim();
var text = ReactDOM.findDOMNode(this.refs.text).value.trim();
if (!text || !author) {
return;
}
// コールバック呼び出し
this.props.onCommentSubmit({author: author, text: text});
ReactDOM.findDOMNode(this.refs.author).value = '';
ReactDOM.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>
);
}
});

最後に、CommentFormにてsubmitされたコメントをCommentBoxコンポーネントでにてサーバへ送信し、stateをリフレッシュするようにする。

var CommentBox = React.createClass({

loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
// state更新
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>
);
}
});


先読み更新

サーバからコメント送信のレスポンスが帰ってくる前にstateをリフレッシュさせ、リスト更新の体感速度をアップさせる。

var CommentBox = React.createClass({

loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
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]);
// コメントをサーバへ送信する前にstateを更新する
this.setState({data: newComments});
$.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) {
this.setState({data: comments});
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>
);
}
});

今回コメントの取得ロジックをCommentBoxに書いたけど、役割で言えばコメント一覧を表示するCommentListコンポーネントに書いた方がいいのでは?と思った。

あと今回はわかりやすい例だったけど、開発ではどのレベルでコンポーネントとして切り出すかとか結構悩みそうな気がする。

コンポーネント化による恩恵とHTMLの管理のしやすさを天秤にかけていく感じかな。


おしまい

React楽しい!