ブログBlog

    • 今からはじめるReact.js〜propsとstate、それからrefs〜

    • 2015年11月12日
    • React
※この記事は http://qiita.com/kuniken/items/a22adda392ccc30011b1 で投稿した内容と同じものです。 前回→今からはじめるReact.js〜React ver0.14〜

propsとstateの特徴

なんらかの一覧表コンポーネントを作る場合、コンポーネントに表示させたいものを指定する必要があるわけですが、React.jsではデータを管理するための2つの変数が用意されています。
  • props
  • state
です。表示させたいものをこれら2つの変数に指定していきます。 2つの変数それぞれの特徴は、
値を指定するタイミング 値の変更可否
props コンポーネント作成時 NO
state コンポーネント作成後 YES
になります。

値の指定方法

props

<Component propsKey1="value1" propsKey="value2"> のようにコンポーネント作成時に指定します。

state

var Component = React.createClass({
  componentDidMount:function(){
    this.setState({stateKey1: "value1",
                   stateKey2: "value2"});
  },
のようにコンポーネント作成後、任意のタイミングで指定します。 (componentDidMountというファンクションはReact.jsが用意したファンクションで、コンポーネント作成後に実行されるようになっています) stateに値を設定する場合は、setStateを使うようにします。 this.state.stateKey1 = "value1" という書き方は認められていないようです。

値の取得

セットされた値を取得する場合は、単純です。
render:function(){
  return (
    <div>
      <span>title:{this.props.propsKey1}</span>
      <img src={this.state.storeKey1}/>
    </div>
  );
}

this.props.~ this.state.~ と書くだけです。

初期値の指定

propsとstateで定義した変数には必ずしも値が指定されない場合もあるかもしれません。 値が指定されていなくてもサービスとしては問題なく後続処理ができるようにしなければなりません。 以下のようにします。

props

var Component = React.createClass({
  getDefaultProps: function() {
    return {
      propsKey1: 'default value'
    };
  },

state

var Component = React.createClass({
  getInitialState: function() {
    return {
      stateKey1: 'initial value'
    };
  },
stateの場合はファンクション名の通り、初期値を設定するイメージですね。

propsの型・必須指定

propsはコンポーネント外部から指定されるため、おかしな値が渡されること、そもそも必須なのに値が渡されないかもしれないことを考慮したほうが良いかもしれません。 propTypesで定義することで型・必須指定を明確化できます。
var Component = React.createClass({
  propTypes:{
    propsKey1: React.PropTypes.string.isRequired,//型:String、指定必須
    propsKey2: React.PropTypes.number            //型:number
  },

詳細は、開発元サイトが詳しいです。 https://facebook.github.io/react/docs/reusable-components.html 不正な値を渡したり、指定必須なのに値を渡していなかったりすると、以下のように、デベロッパーツールのコンソールにエラーが表示されます。 consoleerr

フォームとリストを作成してみる

以上を踏まえて、ボディにフォームとリストを表示できるようにしてみます。 仕様としては、フォームで入力したデータを追加ボタンをクリックすることでリストに追記していく、というシンプルなものです。 body.jsxを書き換えていきます。
//body.jsx
var React = require('react');
var ReactDOM = require('react-dom');

//ボディの定義
var Body = React.createClass({
  render: function(){
    return (
      <UserBox/>
    );
  }
});
BodyではUserBoxコンポーネントを表示するようにしました。 UserBoxコンポーネントは入力フォームとリストをまとめたコンポーネントです。 データのやり取りは下図の通りです。 form
//body.jsx
//フォームとリストを一つにしたもの
var UserBox = React.createClass({
  getInitialState:function(){
    return {userData:[]};
  },
  handleAddUser:function(name, mail){
    var data = this.state.userData;
    data.push({name: name, mail: mail});
    this.setState({userData: data});
  },
  render:function(){
    return(
      <div style={{width:"300px"}}>
        <UserForm addUser={this.handleAddUser}/>
        <hr/>
        <UserList userData={this.state.userData}/>
      </div>
    );
  }
});
UserBoxコンポーネントでは、ユーザーデータの一覧を持てるよう、stateにuserDataを保持するようにしました。 初期値として、getInitialStateで空の配列を代入しておきます。 次にフォームで追加ボタンをクリックしたら、stateのuserDataに値が追加されるようにするためのイベントを作成しました(handleAddUser)。 最後にrenderですが、 handleAddUserがフォームの追加ボタンをクリックした時に呼ばれるようにしたいため、UserFormのpropsであるaddUserにhandleAddUserを指定しています。 また、一覧で表示されるデータとして、UserListのpropsであるuserDataに、UserBoxのstateであるuserDataを指定しています。
//body.jsx
//リストそのものを表示するコンポーネントを定義
var UserList = React.createClass({
  propTypes:{
    userData:React.PropTypes.arrayOf(React.PropTypes.object).isRequired
  },
  render:function(){
    var UserNodes = this.props.userData.map(function(user, index){
      return (
        <User name={user.name} mail={user.mail} key={index}/>
      );
    });
    return (
      <table>
        <tbody>
          <tr>
            <th>名前</th>
            <th>メールアドレス</th>
          </tr>
          {UserNodes}
        </tbody>
      </table>
    );
  }
});
リストを表示するコンポーネントでは、propsであるuserDataについて一行ずつUserコンポーネントとして作成し、積み上げたものをUserNodesという変数に代入するようにしています。 Userコンポーネントは以下の通りです。
//body.jsx
//リスト一行分を表示するコンポーネントを定義
var User = React.createClass({
  propTypes:{
    name: React.PropTypes.string.isRequired,
    mail: React.PropTypes.string
  },
  render:function(){
    return (
      <tr>
        <td>{this.props.name}</td>
        <td>{this.props.mail}</td>
      </tr>
    );
  }
});
propsとして定義された名前とメールアドレスを表示するようにしています。 最後に入力フォームは以下の通りです。
//body.jsx
//ユーザーの入力フォームを定義
var UserForm = React.createClass({
  propTypes:{
    addUser:React.PropTypes.func.isRequired
  },
  handleSubmit:function(){
    var name = ReactDOM.findDOMNode(this.refs.name).value.trim();
    var mail = ReactDOM.findDOMNode(this.refs.mail).value.trim();
    if (!name){
      return;
    }
    this.props.addUser(name, mail);
    ReactDOM.findDOMNode(this.refs.name).value = "";
    ReactDOM.findDOMNode(this.refs.mail).value = "";
  },
  render:function(){
    return (
      <div>
        <table>
          <tbody>
            <tr>
              <td>
                <label>名前</label>
              </td>
              <td>
                <input type="text" ref="name"/>
              </td>
            </tr>
            <tr>
              <td>
                <label>メールアドレス</label>
              </td>
              <td>
                <input type="text" ref="mail"/>
              </td>
            </tr>
          </tbody>
        </table>
        <div style={{textAlign:"right"}}>
          <button onClick={this.handleSubmit}>追加</button>
        </div>
      </div>
    );
  }
});

module.exports = Body;
追加ボタンをクリックした際のonClickイベントとしてhandleSubmitを定義しています。 handleSubmitではinputに入力された値を取得し、propsのaddUserイベントに値を渡しています。 ここでは、ReactDOM.findDOMNodeというのとrefsというのを使っています。

refs

コンポーネントにはそれぞれ、refというコンポーネントを識別するための属性を持つことができます。 同一コンポーネント内の全てのrefはrefsというものにまとめられます。 上記のUserFormコンポーネントを例にすると、 <input type="text" ref="name"/> <input type="text" ref="mail"/> とrefが定義されていますが、UserFormコンポーネントでthis.refs.nameまたはthis.refs.mailと指定すると、それぞれのコンポーネントにアクセスすることができるようになります。 コンポーネントのDOM要素を取得する場合は、ReactDOM.findDOMNodeを使用します。 getDOMNodeというのもあったのですが、Ver0.14で非推奨となっています。
var name = ReactDOM.findDOMNode(this.refs.name).value.trim();
のように使用します。

サンプルソース

https://github.com/kunitak/react-tutorial/tree/day5 次回→今からはじめるReact.js〜サーバーサイド〜

この記事を書いた人 : 國田健史

一覧へ戻る

開発についてのお問い合わせはこちら

お問い合わせ

JOIN OUR TEAM

積極採用しています。私たちと一緒に働きませんか?