Help us understand the problem. What is going on with this article?

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楽しい!

saekis
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away