React


フォーム


9.1 管理されていないコンポーネント(Uncontrolled component)

管理されていないコンポーネントは、単純なフォームを作成するときには使わない

DOMノードに直接アクセスする

フォームは他の React コンポーネントとは異なる動作をします。 input要素に値が設定されるとそれ以後、値の変更は input 要素により管理されます。 そのため React コンポーネントが値を管理することはできません。

  // MyForm01
  var MyForm01 = React.createClass({
    render: function(){
      return <input
        type="text"
        defaultValue="Hello World!"/>;
    }
  });

  React.render(
    ,
    document.getElementById('myform01')
  );

フォームの値にアクセスするためにはinput要素にref属性を設定し、 DOMノードに直接アクセスします

  // MyForm02
  var MyForm02 = React.createClass({
    submitHandler: function(event){
      event.preventDefault();
      // ref 経由でアクセス
      var helloTo = React.findDOMNode(this.refs.helloTo).value;
      alert(helloTo);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <input
        ref="helloTo"
        type="text"
        defaultValue="Hello World!"
        />
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.2 管理されたコンポーネント

親コンポーネントのstateに値が保存されている

データ―フロー
  1. getInitialState で初期値を設定
  2. rnderメソッドでinput要素のvalueが設定される
  3. 値が変更されると input要素のonChageが呼ばれる
  4. ハンドラ内でstateが更新される
  5. 再びrenderメソッドが呼ばれ input要素が更新される
  var MyForm03 = React.createClass({
    getInitialState: function(){
      return {
        helloTo: 'Hello World!'
      }
    },
    handleChange: function(event){
      this.setState({
        helloTo: event.target.value
      });
    },
    submitHandler: function(event){
      event.preventDefault();
      alert(this.state.helloTo);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <input
        type="text"
        value={this.state.helloTo}
        onChange={this.handleChange}
        />
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.3 フォームのイベント

イベントはフォームの状態をコントロールするのに欠かせないもの。

React は HTML で発生するすべてのイベントを独自のイベントオブジェクトに変換します。

すべてのイベントオブジェクトは、 target プロパティ経由でイベントの発生元となる DOMノードへアクセスできる。

handleEvent: function(suntheticEvent){
  var DOMNode = syntheticEvent.target;
  var newValue = DOMNode.value;
}

イベントオブジェクトのtargetを参照することで、 管理されたコンポーネントにおいては ユーザーの入力値にアクセスできます。

9.4 ラベル

HTMLのlabel要素には for 属性がありますが JSXでは htmlFor と指定します

<label htmlFor="name">名前:</label>

9.5 textarea と select

React では textarea と select のインターフェース仕様がオリジナルのHTMLから変更されています

Reactの textarea コンポーネントは、 value, defaultValueを指定することができます

// 管理されていないコンポーネント
<textarea defaultValue="Hello World"></textarea>
// 管理されているコンポーネント
<textarea>
  value={this.state.helloTo};
  onChage={this.handleChange};
</textarea>

Reactの selectコンポーネントは、 value と defaultValueによって、 どのoption値が選択されているか指定できる。 これによりHTMLのselect要素よりも外部から簡単に操作ができる

// 管理されていないコンポーネント
<select defaultValue="B">
  <option value="A">選択肢A</option>
  <option value="B">選択肢B</option>
  <option value="C">選択肢C</option>
</select>
// 管理されているコンポーネント
<select defaultValue={this.state.helloTo} onChange={this.handleChange}>
  <option value="A">選択肢A</option>
  <option value="B">選択肢B</option>
  <option value="C">選択肢C</option>
</select>

Reactの selectコンポーネントは、複数選択も可能。

// 管理されていないコンポーネント
<select multiple="true" defaultValue={["A","B"]}>
  <option value="A">選択肢A</option>
  <option value="B">選択肢B</option>
  <option value="C">選択肢C</option>
</select>
// 管理されているコンポーネント
<select defaultValue={this.state.helloTo} onChange={this.handleChange}>
  <option value="A">選択肢A</option>
  <option value="B">選択肢B</option>
  <option value="C">選択肢C</option>
</select>

複数選択が有効の場合、<select>コンポーネントのvalue属性は optionの選択が変更されても、 **更新されません**

子コンポーネントとなる<option>要素のselectedプロパティが変更されます。 option要素にref属性を設定するか、 イベントハンドラ内で、イベントオブジェクトのtargetプロパティを参照して 選択されたoption要素にアクセスする

  var MyForm04 = React.createClass({
    getInitialState: function(){
      return {
        options: ["B"]
      }
    },
    handleChange: function(event){
      var checked =[];
      var sel = event.target;
      for (var i=0; i<sel.length;i++){
        var option = sel.options[i];
        if (option.selected){
          checked.push(option.value);
        } 
      }
      this.setState({
        options: checked
      });
    },
    submitHandler: function(event){
      event.preventDefault();
      alert(this.state.options);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <select multiple="true"   
            value={this.state.options} 
            onChange={this.handleChange}>
          <option value="A">選択肢A</option>
          <option value="B">選択肢B</option>
          <option value="C">選択肢C</option>
        </select>
 
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.6 ラジオボタン と チェックボックス

<input>要素はtype属性により動作が異なります。

type="text"の場合、値が変化します

type="checkbox", type="radio" の場合、値は変化せず、 checkedの状態のみ変化します。

// 管理されていないちぇえっくボックス
var MyForm = react.createClass({
  submitHandler: function(event){
    event.preventDefault();
    alert(React.findDOMNode(this.refs.checked).checked);
  },
  render: function(){
    return <form onSubmit={this.submitHandler}>
        <input
          ref="checked"
          type="checkbox"
          value="A"
          defaultChecked="true" />
        <br />
        <button type="submit">送信</button>  
      </form>;
  }
});
  var MyForm05 = React.createClass({
    getInitialState: function(){
      return {
        checked: true
      }
    },
    handleChange: function(event){
      this.setState({
        checked: event.target.checked
      });
    },
    submitHandler: function(event){
      event.preventDefault();
      alert(this.state.checked);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <input
          type="checkbox"
          checked={this.state.checked}
          onChange={this.handleChange}
        />
 
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.7 フォーム要素の name属性

Reactではフォーム要素のname属性は重要ではないが、 フォームのコンポーネントにおいて欠かせない場面がある。

管理されていないラジオボタンのデフォルト動作をイベントハンドラで実現した例。 name属性は使用していない。

  var MyForm06 = React.createClass({
    getInitialState: function(){
      return {
        radio: "B"
      }
    },
    handleChange: function(event){
      this.setState({
        radio: event.target.value
      });
    },
    submitHandler: function(event){
      event.preventDefault();
      alert(this.state.radio);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <input
          type="radio"
          value="A"
          checked={this.state.radio=="A"}
          onChange={this.handleChange} />A
        <br />
        <input
          type="radio"
          value="B"
          checked={this.state.radio=="B"}
          onChange={this.handleChange} />B
        <br />
        <input
          type="radio"
          value="C"
          checked={this.state.radio=="C"}
          onChange={this.handleChange} />C
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.8 複数のフォーム要素と change イベントハンドラ

管理されたフォーム要素を作成する際に、すべての要素にchangeイベントハンドラを記述するのは苦痛。

Reactではchangeハンドラを再利用する方法がいくつかあります

9.8.1 bind経由でイベントハンドラに追加の引数を渡す

  var MyForm07 = React.createClass({
    getInitialState: function(){
      return {
        given_name: "",
        family_name: ""
      }
    },
    handleChange: function(name,event){
      var newState = {};
      newState[name]= event.target.value;
      this.setState(newState);
    },
    submitHandler: function(event){
      event.preventDefault();
      var words = [
        "Hi",
        this.state.given_name,
        this.state.family_name
      ];
      alert(words.join(" "));
    },
    render: function(){
      return <form onSubmit={this.submitHandler}>
        <label htmlFor="given_name">名:</label>
        <br />
        <input
          type="text"
          name="given_name"
          value={this.state.given_name}
          onChange={this.handleChange.bind(this,'given_name')} />
        <br />
        <label htmlFor="family_name">姓:</label>
        <br />
        <input
          type="text"
          name="family_name"
          value={this.state.family_name}
          onChange={this.handleChange.bind(this,'family_name')} />
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.8.2 イベントハンドラ内でDOMノードのname属性を参照する

  var MyForm08 = React.createClass({
    getInitialState: function(){
      return {
        given_name: "",
        family_name: ""
      }
    },
    handleChange: function(event){
      var newState = {};
      newState[event.target.name]= event.target.value;
      this.setState(newState);
    },
    submitHandler: function(event){
      event.preventDefault();
      var words = [
        "Hi",
        this.state.given_name,
        this.state.family_name
      ];
      alert(words.join(" "));
    },
    render: function(){
      return <form onSubmit={this.submitHandler}>
        <label htmlFor="given_name">名:</label>
        <br />
        <input
          type="text"
          name="given_name"
          value={this.state.given_name}
          onChange={this.handleChange} />
        <br />
        <label htmlFor="family_name">姓:</label>
        <br />
        <input
          type="text"
          name="family_name"
          value={this.state.family_name}
          onChange={this.handleChange} />
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.9 カスタムフォームコンポーネント

 var Radio = React.createClass({
    propTypes: {
      onChange: React.PropTypes.func
    },
    getInitialState: function(){
      return {
        value: this.props.defaultValue
      };
    },
    handleChange: function(event){
      if (this.props.onChange){
        this.props.onChange(event);
      }
      this.setState({
        value: event.target.value
      });
    },
    render: function(){
      var children = {};
      var value = this.props.value || this.state.value;

      React.Children.forEach(this.props.children, function(child, i){
        var label = <label>
            <input
              type="radio"
              name={this.props.name}
              value={child.props.value}
              checked={child.props.value==value}
              onChange={this.handleChange} />
            {child.props.children}
            <br />
          </label>
        children['label'+i]=label;    
      }.bind(this));
      return <span>{children}</span>;
    }
  });

管理されないコンポーネント

  var MyForm09 = React.createClass({
    submitHandler: function(event){
      event.preventDefault();
      alert(this.refs.radio.state.value);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <Radio ref="radio" name="my_radio" defaultValue="B">  
          <option value="A">選択肢  A</option>
          <option value="B">選択肢  B</option>
          <option value="C">選択肢  C</option>
        </Radio>
 
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

管理されたコンポーネント

var MyForm10 = React.createClass({
    getInitialState: function(){
      return {
        my_radio: "B"
      }
    },
    handleChange: function(event){
      this.setState({
        my_radio: event.target.value
      });
    },
    submitHandler: function(event){
      event.preventDefault();
      alert(this.state.my_radio);
    },
    render: function(){
      return <form onSubmit={this.submitHandler}> 
        <Radio name="my_radio" 
            value={this.state.my_radio}
            onChange={this.handleChange}
        >  
          <option value="A">選択肢 A</option>
          <option value="B">選択肢 B</option>
          <option value="C">選択肢 C</option>
        </Radio>
 
        <br />
        <button type="submit">送信</button>
        </form>;
    }
  });

9.10 フォーカス

<input type="text" name="given_name" autoFocus="true">

手動でフォーカスを設定するには、 DOMノードにアクセスして focus()メソッドを呼び出します。

9.1 ユーザビリティ