ReactでできるだけシンプルにForm実装

Reactのフォーム実装で消耗したのでライブラリ等使わずにできるだけシンプルにフォーム実装する方法考えてみた。

# 方針
- 項目ごとのリアルタイムなバリデーションやエラー表示はしっかりやる
- stateに入力値を入れない
- できるだけform本来の機能を使う
- IE11以上に対応
- FormDataは使わない（IEがentries()やkeys()に対応していないため）

# 前提環境
- Babel
- ES2017
- object-rest-spreadを使用

# サンプル
実際に動かせるサンプルはJSFiddleに作ってるのでこちらからどぞ。
https://jsfiddle.net/3ywe2tmk/33/

## コード
```jsx
const FIELDS = {
  userId: 'userId',
  age: 'age',
};

class Form extends React.Component {
  constructor() {
    super();

    this.state = {
      errors: {},
      isSubmitEnabled: false,
    };
    
    this.elements = null;
    
    this.handleFormChange = this.handleFormChange.bind(this);
    this.handleFormBlur = this.handleFormBlur.bind(this);
    this.handleFormSubmit = this.handleFormSubmit.bind(this);
  }

  validate(field, value) {
    if (!value) {
      return true;
    }

    let isValid = true;
    switch (field) {
      case FIELDS.userId:
        isValid = /^[0-9a-zA-Z]+$/.test(value);
        break;
      case FIELDS.age:
        isValid = /^[0-9]+$/.test(value);
        break;
      default:
        break;
    }

    return isValid;
  }
  
  setSubmitEnabled() {
    if (!this.elements) {
      return;
    }

    const isNoEmptyFields = Object.values(FIELDS).every(field => this.elements[field].value);
    const isNoErrors = Object.values(this.state.errors).every(error => !error);

    this.setState({
      isSubmitEnabled: isNoEmptyFields && isNoErrors,
    });
  }

  handleFormChange() {
    this.setSubmitEnabled();
  }
  
  handleFormBlur(event) {
    const field = event.target.name;

    // validation
    this.setState({
      errors: {
        ...this.state.errors,
        [field]: !this.validate(field, event.target.value),
      },
    }, () => {
      this.setSubmitEnabled();
    });
  }
  
  handleFormSubmit(event) {
    event.preventDefault();
    
    
    // form値取得
    const params = {
      userId: this.elements[FIELDS.userId].value,
      age: this.elements[FIELDS.age].value,
    };
    
    alert(JSON.stringify(params, null, '  '));
  }

  render() {
    const { errors } = this.state;
    return (
      <form
        onChange={this.handleFormChange}
        onBlur={this.handleFormBlur}
        onSubmit={this.handleFormSubmit}
        ref={el => this.elements = el && el.elements}
      >
        <div>
          <span className="label">ユーザーID</span>
          <input
            type="text"
            name={FIELDS.userId}
            />
        </div>
        {
          this.state.errors[FIELDS.userId] &&
            <p className="error">
              半角英数字で入力してください。
            </p>
        }
        <div>
          <span className="label">年齢</span>
          <input
            type="text"
            name={FIELDS.age}
            />
        </div>
        {
          this.state.errors[FIELDS.age] &&
            <p className="error">
              半角数字で入力してください。
            </p>
        }
        <button
          type="submit"
          disabled={!this.state.isSubmitEnabled}
          >submit</button>
      </form>
    );
  }
}

ReactDOM.render(
  <Form/>,
  document.getElementById('container')
);
```

## ポイント
### form.elements
form.elementsから入力値を参照。
stateに入力値を溜め込まなくてもここからkey指定で入力値を取得できます。
例： `form.elements.userId.value`
https://developer.mozilla.org/ja/docs/Web/API/HTMLFormElement/elements
### onChange
formのonChangeでフォーム全体の入力値変更を検知。
### onBlur
formのonBlurでフォーム全体のblurを検知してvalidationを実行。
### onSubmit
formのonSubmitでsubmitを検知してform.elementsから入力値を取得。