Formコンポーネントを作る
Formコンポーネントは掲示板アプリの入力部分です。名前の入力欄(input要素)、年齢の入力欄(select要素)、本文(textarea要素)の3個の入力欄と、送信ボタン(input要素)からなり、form要素で囲まれている、古典的なHTMLのフォームです(図4)。
また、stateとpropsは次のような構造です。
| 名前 | 型 | 備考 |
|---|---|---|
| name | string | - |
| age | string | Agesからひとつ選択 |
| body | string | - |
Agesは次のように、年齢の選択肢を提供するオブジェクトです。
export const Ages = {
TEEN: "teen",
TWENTIES: "twenties",
THIRTIES: "thirties",
FOURTIES: "fourties",
FIFTIES: "fifties",
}
Listコンポーネントでも使用するため、exportしてあります。
| 名前 | 型 |
|---|---|
| onSubmitNewPost | ({ name, age, body }) => void |
onSubmitNewPostは、前述の通りAppコンポーネントに投稿データを送る関数オブジェクトです。
form要素の扱い
Reactでform要素を扱う際には、次の方針が採用されることが多いです。
- stateが持つ状態を入力欄に反映する。
- 各入力欄への入力内容をstateへ随時保存する。
- 送信ボタンが押された時点でのstateを送信対象として取りまとめて、propsの関数に渡す。
form要素が本来持っている送信などの機能は使わずに、JavaScript側でイベントをハンドリングする形になります。
それでは、コンポーネントの実装を見ていきましょう。
単一行テキストへの入力を扱う
まずは、入力値をstateに反映する方法について、名前の入力欄を例に挙げて解説します。JSXとしてはtype属性をtextにしたinput要素を使って、次のような形で実装します。
<div>
<label>
名前:
<input
type="text"
value={this.state.name}
onChange={(event) => this.handleChangeName(event)} />
</label>
</div>
今回の実装方針では、フォームに表示されるべき正しい値をstateが持っています。そのため、stateの内容に沿ってinput要素が表示されるよう、value属性にstateの内容を渡しています。
そして、入力があった場合には、onChangeに登録されたコールバックが呼ばれます。event.target.valueにDOMから渡された入力値が入っているので、そのままstateに登録すればOKです。
handleChangeName(event) {
this.setState({
name: event.target.value
});
}
セレクトボックスの選択を扱う
select要素を扱う場合も、input要素の場合とほとんど変わりません。JSXは次のように実装します。
<div>
<label>
年齢:
<select
value={this.state.age}
onChange={(event) => this.handleChangeAge(event)}>
<option value={Ages.TEEN}>10代</option>
<option value={Ages.TWENTIES}>20代</option>
<option value={Ages.THIRTIES}>30代</option>
<option value={Ages.FOURTIES}>40代</option>
<option value={Ages.FIFTIES}>50代</option>
</select>
</label>
</div>
また、コールバックは次のように実装します。event.target.valueに、option要素のvalue属性に指定してあった値が渡される点に注目してください。
handleChangeAge(event) {
this.setState({
// option要素のvalueが渡される
age: event.target.value
});
}
複数行テキストへの入力を扱う
textarea要素を扱う場合は、input要素の場合とまったく同じ扱いをします。JSXは次のように実装します。
<div>
<label>
本文:
<textarea
value={this.state.body}
onChange={(event) => this.handleChangeBody(event)} />
</label>
</div>
また、コールバックは次のように実装します。
handleChangeBody(event) {
this.setState({
body: event.target.value
});
}
3種類の入力欄について説明してきました。通常のフォームの扱いとあまり変わりませんね。これで、入力された値を常にstateに保持しておけるようになりました。
送信ボタンのイベントハンドリングを行う
stateに値が入ったら、あとは送信ボタンを押すだけです。次のように、送信のinput要素は通常のHTMLと変わりありません。
<input type="submit" value="送信" />
一方で、form要素の扱いは少し特殊です。送信先のURLなどはここには登録せず、次のようにonSubmitを使ってイベントを拾います。
<form onSubmit={(event) => this.handleSubmit(event)}>
コールバックの実装は次のように行いました。
handleSubmit(event) {
// submitのデフォルト動作では
// 画面が再描画されてしまうので阻止する
event.preventDefault(); // (1)
const { name, age, body } = this.state;
// 投稿する
this.props.onSubmitNewPost({ // (2)
name,
age,
body,
});
// フォームの内容をリセットする
this.setState({
...this.createInitialForm()
});
}
onSubmitのイベントハンドリングで最も重要なのは、(1)のpreventDefault呼び出しです。これを実行しなかった場合、form要素のデフォルトの動作により、ページがリロードされてしまいます。シングルページアプリケーションとしては致命的な挙動になるので、忘れずに呼び出しましょう。
十分にバリデーションを行ったら、最後に送信用の関数を呼び出します(2)。これで、Appコンポーネントに送信内容が渡されました。
