出典:https://react.docschina.org/docs/forms.html
一、フォーム#
React では、html フォーム要素の動作は他の DOM 要素とは少し異なります。なぜなら、フォーム内部は通常いくつかの内部のstate
を保持するからです。例えば、以下の純粋な html フォームは名前を 1 つだけ受け付けます:
<form>
<label>
名前:
<input type="text" name="name" />
</label>
<input type="submit" value="送信" />
</form>
このフォームはデフォルトの html フォームの動作を持っており、ユーザーがフォームを送信した後に自動的にページをリフレッシュします。もし React で同じコードを実行しても、それは有効です。
しかし、ほとんどの場合、JavaScript 関数を使用してフォームの送信を便利に処理し、ユーザーが入力したフォームデータにアクセスすることができます。この効果を実現する標準的な方法は「制御されたコンポーネント」を使用することです。
制御されたコンポーネント#
html では、フォーム要素(例えば<input>
、<textarea>
、<select>
)は通常自分自身でstate
を管理し、ユーザーの入力に応じて更新します。しかし、React では、可変状態(mutable state)は通常コンポーネントの state 属性に保存され、setState()
を使用してのみ更新できます。
私たちは両者を組み合わせて、React の state を「唯一のデータソース」とすることができます。フォームをレンダリングする React コンポーネントは、ユーザー入力の過程でフォームで発生する操作も制御します。React によってこのように制御されるフォーム入力要素は「制御されたコンポーネント」と呼ばれます。
例えば、前の例で送信時に名前を印刷したい場合、フォームを制御されたコンポーネントとして書くことができます:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('送信された名前: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名前:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="送信" />
</form>
);
}
}
フォーム要素にvalue
属性が設定されているため、表示される値は常にthis.state.value
となり、これにより React の state が唯一のデータソースとなります。handleChange
は毎回キーを押すたびに実行され、React の state を更新するため、表示される値はユーザーの入力に応じて更新されます。
制御されたコンポーネントでは、入力の値は常に React の state によって駆動されます。あなたは value を他の UI 要素に渡したり、他のイベント処理関数を通じてリセットしたりすることもできますが、これはより多くのコードを書く必要があることを意味します。
textarea タグ#
html では、<textarea>
要素はその子要素によってテキストを定義します:
<textarea>
テスト!
</textarea>
しかし、React では、<textarea>
はvalue
属性を使用します。これにより、<textarea>
を使用するフォームは単一行の input を使用するフォームと非常に似たものになります:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'あなたの好きなDOM要素についてのエッセイを書いてください。'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('送信されたエッセイ: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
エッセイ:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="送信" />
</form>
);
}
}
注意してください、this.state.value
はコンストラクタ内で初期化されているため、テキストエリアにはデフォルト値があります。
select タグ#
HTML では、<select>
はドロップダウンリストを作成します。例えば、以下の html は果物に関連するドロップダウンリストを作成します:
<select>
<option value="grapefruit">グレープフルーツ</option>
<option value="lime">ライム</option>
<option selected value="coconut">ココナッツ</option>
<option value="mango">マンゴー</option>
</select>
selected
属性のため、ココナッツオプションはデフォルトで選択されています。React はselected
属性を使用せず、ルートselect
タグでvalue
属性を使用します。これは制御されたコンポーネントでは便利で、ルートタグでそれを更新するだけで済みます。例えば:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('あなたの好きなフレーバーは: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
あなたの好きなフレーバーを選択してください:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">グレープフルーツ</option>
<option value="lime">ライム</option>
<option value="coconut">ココナッツ</option>
<option value="mango">マンゴー</option>
</select>
</label>
<input type="submit" value="送信" />
</form>
);
}
}
全体として、これにより<input type="text">
、<textarea>
、および<select>
のようなタグは非常に似たものになります — それらはすべて value 属性を受け入れ、制御されたコンポーネントを実現するために使用できます。
注意:あなたは配列を value 属性に渡して、select タグで複数のオプションを選択することをサポートできます:
<select multiple={true} value={['B', 'C']}>
ファイル input タグ#
HTML では、<input type="file">
はユーザーがストレージデバイスから 1 つまたは複数のファイルを選択し、それをサーバーにアップロードすることを許可します。また、JavaScript のFile API
を使用して制御することもできます。
<input type="file" />
その value は読み取り専用なので、React では非制御コンポーネントです。
複数の入力を処理する#
複数のinput
要素を処理する必要がある場合、各要素にname
属性を追加し、処理関数がevent.target.name
の値に基づいて実行する操作を選択できるようにします。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.name === 'isGoing' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参加:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
ゲストの人数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
ここでは ES6 の計算プロパティ名
の構文を使用して、指定された入力名に対応する state 値を更新しています:
例えば:
this.setState({
[name]: value
});
これは es5 では次のようになります:
var partialState = {};
partialState[name] = value;
this.setState(partialState);
さらに、setState()
は自動的に部分的な state を現在の state にマージするため、部分的な state を変更するにはそれを呼び出すだけで済みます。
制御された入力の空値#
制御されたコンポーネントで value の prop を指定すると、ユーザーが入力を変更するのを防ぎます。もしvalue
を指定したのに入力が依然として編集可能であれば、あなたが意図せずにvalue
をundefined
またはnull
に設定した可能性があります。
以下のコードはこれを示しています。(入力は最初はロックされていますが、短時間の遅延後に編集可能になります。)
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
制御されたコンポーネントの代替品#
時には制御されたコンポーネントを使用するのが面倒なことがあります。なぜなら、データの変化のためのすべての方法に対してイベント処理関数を書く必要があり、すべての入力 state を 1 つの React コンポーネントを通じて渡す必要があるからです。以前のコードベースを React に変換したり、React アプリケーションを非 React ライブラリと統合したりする際に、これは煩わしいことがあります。このような場合、非制御コンポーネントを使用したいかもしれません。これは入力フォームを実現する別の方法です。
成熟した解決策#
検証、フィールドの追跡、およびフォームの送信を処理する完全な解決策を探している場合、Formik を使用するのは良い選択です。しかし、それも制御されたコンポーネントと state 管理の上に構築されているため、これらを学ぶことを無視しないでください。