react.js

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

More than 1 year has 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楽しい!