Reactのフォーム実装で消耗したのでライブラリ等使わずにできるだけシンプルにフォーム実装する方法考えてみた。
方針
- 項目ごとのリアルタイムなバリデーションやエラー表示はしっかりやる
- stateに入力値を入れない
- できるだけform本来の機能を使う
- IE11以上に対応
- FormDataは使わない(IEがentries()やkeys()に対応していないため)
前提環境
- Babel
- ES2017
- object-rest-spreadを使用
サンプル
実際に動かせるサンプルはJSFiddleに作ってるのでこちらからどぞ。
https://jsfiddle.net/3ywe2tmk/33/
コード
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から入力値を取得。