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

React.jsでFormを扱う

More than 5 years have passed since last update.

今回はReact.jsでのFormの扱いについて書きたいと思います。

例えばReact.jsでは↓のようなことをすると変更出来ないテキストフィールドになってしまいます。どうしてなのかという話ですね。

// ダメ
<input type="text" value="initial value" />

// ダメ
<input type="text" value={this.state.textValue} />

http://jsfiddle.net/koba04/kb3gN/8198/

Controlled Component

Controlled ComponentとはStateによって値を管理するComponentのことでテキストフィールドの場合はこのようになります。

var Text = React.createClass({
  getInitialState() {
    return {
      textValue: "initial value"
    };
  },
  changeText(e) {
    this.setState({textValue: e.target.value});
  },
  render() {
    return (
      <div>
        <p>{this.state.textValue}</p>
        <input type="text" value={this.state.textValue} onChange={this.changeText} />
      </div>
    );
  }
});

value値をStateで管理しつつ、onChangeで明示的にsetStateして更新してあげる必要があります。

UnControlled Component

UnControlled Componentとは逆に値を管理しないComponentで、初期値を設定した場合はdefaultValueに設置する必要があります。

この場合は↑のようにonChangeで常にStateに反映してもいいですし、反映したい時にDOMからvalueを取得することも出来ます。

var LiveText = React.createClass({
  getInitialState() {
    return {
      textValue: "initial value"
    };
  },
  changeText(e) {
    this.setState({textValue: this.refs.inputText.getDOMNode().value });
  },
  render() {
    return (
      <div>
        <p>{this.state.textValue}</p>
        <input type="text" ref="inputText" defaultValue="initial value" />
        <button onClick={this.changeText}>change</button>
      </div>
    );
  }
});

textarea

テキストエリアの場合は、テキストフィールドと同じようにvalueに値を指定します。
HTMLの時と同じように<textarea>xxxx</textarea>とするとそれはdefaultValueとして扱われます。

var OreTextArea = React.createClass({
  getInitialState() {
    return {
      textAreaValue: "initial value"
    };
  },
  onChangeText(e) {
    this.setState({textAreaValue: e.target.value});
  },
  onClick() {
    this.setState({textAreaValue: this.refs.textArea.getDOMNode().value}); 
  },
  render() {
    return (
      <div>
        <div>{this.state.textAreaValue}</div>
        <div>
          <textarea value={this.state.textAreaValue} onChange={this.onChangeText} />
        </div>
        <div>
          <textarea ref="textArea">this is default value</textarea>
          <button onClick={this.onClick}>change</button>
        </div>
      </div>
    );
  }
});

http://jsfiddle.net/koba04/wkkvrh4m/2/

select

セレクトボックスの場合もvalueに値を設定します。multiple={true}のPropを指定するとvalueに配列を指定できるようになり、複数要素を選択することができるようになります。

var OreSelectBox = React.createClass({
  getDefaultProps() {
    return {
      answers: [1, 10, 100, 1000]
    };
  },
  getInitialState() {
    return {
      selectValue: 1,
      selectValues: [1,100]
    };
  },
  onChangeSelectValue(e) {
    this.setState({selectValue: e.target.value});
  },
  // 他に良い方法があるかもい...
  onChangeSelectValues(e) {
    var values = _.chain(e.target.options)
      .filter(function(option) { return option.selected })
      .map(function(option) { return +option.value })
      .value()
    ;
    this.setState({selectValues: values});
  },
  render() {
    var options = this.props.answers.map(function(answer) {
      return <option value={answer} key={answer}>{answer}</option>;
    });
    return (
      <div>
        <div>selectValue: {this.state.selectValue}</div>
        <div>
          <select value={this.state.selectValue} onChange={this.onChangeSelectValue}>
            {options}
          </select>
        </div>
        <div>selectValues: {this.state.selectValues.join(",")}</div> 
        <div>
          <select multiple={true} defaultValue={this.state.selectValues} onChange={this.onChangeSelectValues}>
            {options}
          </select>
        </div>     
      </div>
    );
  }
});

http://jsfiddle.net/koba04/khdftsuu/

LinkedStateMixin

LinkedStateMixinというaddonを使うとこんな感じでonChangeを実装しなくてもstateに反映することも出来ます。
CheckBoxに対して使用する場合は、checkedLinkとなります。

var React = require('react/addons');
var LinkedStateMixin = React.createClass({
  mixins: [React.addons.LinkedStateMixin],
  getInitialState() {
    return {
      textValue: "initial value"
    }
  },
  render() {
    return (
      <div>
        <div>value: {this.state.textValue}</div>
        <input type="text" valueLink={this.linkState('textValue')} />
      </div>
    );
  }
});

このMixinがやっていることは簡単ですし、中を見るきっかけとして追ってみるのも面白いかと思います。

蛇足(LinkedStateMixinの挙動がどうなってるのか確認してみる)

  • まずMixinされるlinkStateを見てみると、ReactLinkというオブジェクトをvalueと何か作成されたSetterが渡されて作成しているのがわかります。
  linkState: function(key) {
    return new ReactLink(
      this.state[key],
      ReactStateSetters.createStateKeySetter(this, key)
    );
  }

https://github.com/facebook/react/blob/3aa56039c60add45eb30f1edbaf40ddf195c54ce/src/addons/link/LinkedStateMixin.js#L31-L36

  • ReactStateSetters.createStateKeySetterの中を見てみると、渡したStateのkeyに対してsetStateする関数を作成しています。
  createStateKeySetter: function(component, key) {
    // Memoize the setters.
    var cache = component.__keySetters || (component.__keySetters = {});
    return cache[key] || (cache[key] = createStateKeySetter(component, key));
  }
};

function createStateKeySetter(component, key) {
  // Partial state is allocated outside of the function closure so it can be
  // reused with every call, avoiding memory allocation when this function
  // is called.
  var partialState = {};
  return function stateKeySetter(value) {
    partialState[key] = value;
    component.setState(partialState);
  };
}

https://github.com/facebook/react/blob/3aa56039c60add45eb30f1edbaf40ddf195c54ce/src/core/ReactStateSetters.js#L45-L61

  • ReactLinkのConstructorでは値とrequestChangeとしてcreateStateKeySetterで作成した関数を保持されています。
function ReactLink(value, requestChange) {
  this.value = value;
  this.requestChange = requestChange;
}

https://github.com/facebook/react/blob/3aa56039c60add45eb30f1edbaf40ddf195c54ce/src/addons/link/ReactLink.js#L44-L47

  • ここで、ReactLinkを渡しているvalueLinkのPropに注目してみると、requestChangee.target.valueが渡されて呼ばれることがわかります。
function _handleLinkedValueChange(e) {
  /*jshint validthis:true */
  this.props.valueLink.requestChange(e.target.value);
}

/**
  * @param {SyntheticEvent} e change event to handle
  */
function _handleLinkedCheckChange(e) {
  /*jshint validthis:true */
  this.props.checkedLink.requestChange(e.target.checked);
}

https://github.com/facebook/react/blob/3aa56039c60add45eb30f1edbaf40ddf195c54ce/src/browser/ui/dom/components/LinkedValueUtils.js#L58-L69

  • InputのComponentを見てみると、onChangeイベントでvalueLinkがあれば、_handleLinkedValueChangeを呼ばれて結果setStateされることがわかるかと思います。
  getOnChange: function(input) {
    if (input.props.valueLink) {
      _assertValueLink(input);
      return _handleLinkedValueChange;
    } else if (input.props.checkedLink) {
      _assertCheckedLink(input);
      return _handleLinkedCheckChange;
    }
    return input.props.onChange;
  }

https://github.com/facebook/react/blob/3aa56039c60add45eb30f1edbaf40ddf195c54ce/src/browser/ui/dom/components/LinkedValueUtils.js#L140-L149

  _handleChange: function(event) {
    var returnValue;
    var onChange = LinkedValueUtils.getOnChange(this);
    if (onChange) {
      returnValue = onChange.call(this, event);
    }

https://github.com/facebook/react/blob/3aa56039c60add45eb30f1edbaf40ddf195c54ce/src/browser/ui/dom/components/ReactDOMInput.js#L114-L119


最後はちょっと脱線しましたが、今回はFormの操作について書きました。

明日はReact.jsのVirtualDOM実装で重要な役割を担っているkey属性について書きたいと思います。

koba04
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした