React useStateで入力フォームを作成する
Reactでテキストボックスに入力した値を使用するには、useStateで値を保持します。
今回は名前とメールアドレスを入力し、送信ボタンを押すと入力内容を表示するフォームを作成します。
この例では入力値をReactのstateに置く「制御されたコンポーネント」を使用します。入力のたびに検証や表示を変えたい小さなフォームでは扱いやすい一方、項目数が多く性能や検証管理が問題になる場合は、フォームライブラリや非制御コンポーネントも選択肢になります。
1つの入力値を保持する
最初に名前だけを入力できるコンポーネントを作成します。
NameInput.tsximport { useState } from 'react';
export const NameInput = () => {
const [name, setName] = useState('');
return (
<div>
<label htmlFor="name">名前</label>
<input
id="name"
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
/>
<p>入力中の名前: {name}</p>
</div>
);
};useState('')は、初期値が空文字のstateを作成します。
nameには現在の値、setNameには値を変更する関数が入ります。
テキストボックスの内容が変わるたびにonChangeが実行され、event.target.valueをstateへ保存します。
複数の入力値をオブジェクトで保持する
入力項目が増えた場合は、フォーム全体を1つのオブジェクトで保持できます。
ContactForm.tsximport { useState, type ChangeEvent, type FormEvent } from 'react';
type FormData = {
name: string;
email: string;
};
const initialFormData: FormData = {
name: '',
email: '',
};
export const ContactForm = () => {
const [formData, setFormData] = useState(initialFormData);
const [submittedData, setSubmittedData] = useState<FormData>();
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setFormData((current) => ({
...current,
[name]: value,
}));
};
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSubmittedData(formData);
};
return (
<>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">名前</label>
<input
id="name"
name="name"
value={formData.name}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="email">メールアドレス</label>
<input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<button type="submit">送信</button>
</form>
{submittedData && (
<p>
{submittedData.name}さんのメールアドレスは
{submittedData.email}です。
</p>
)}
</>
);
};inputのnameを、FormDataのプロパティ名と同じにしています。
setFormDataでは、スプレッド構文の...currentで現在の値をコピーしてから、変更された項目だけを上書きします。
preventDefaultで画面の再読み込みを防ぐ
HTMLのフォームは、送信すると通常はページを再読み込みします。
React内で送信処理を行う場合は、event.preventDefault()で標準の送信動作を止めます。
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSubmittedData(formData);
};buttonのtype="submit"を押すと、formのonSubmitが実行されます。
Enterキーで送信した場合も同じ処理を通るため、ボタンのonClickだけに送信処理を書くより扱いやすくなります。
必須入力を確認する
簡単な必須チェックなら、HTMLのrequired属性を使用できます。
<input
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
/>空のまま送信すると、ブラウザが入力を求めるメッセージを表示します。
複雑な条件やサーバーから返されたエラーを表示する場合は、エラーメッセージ用のstateやフォームライブラリを使用します。
APIへ送信する
入力内容をAPIへ送る場合は、送信処理をasyncにします。
APIへ送信する例const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const response = await fetch('/api/contacts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
if (!response.ok) {
setMessage('送信に失敗しました。');
return;
}
setMessage('送信しました。');
setFormData(initialFormData);
};fetchでは、400や500のレスポンスでも自動的には例外になりません。response.okを確認します。
APIへ送信する場合は送信中のstateも用意し、処理が終わるまで送信ボタンを無効にします。
まずはvalueとonChangeで入力値をstateへ保存し、onSubmitでまとめて処理する流れを覚えると、Reactのフォームを作りやすくなります。