Menu

Reactのフォーム:入力欄、チェックボックス、select、バリデーション

制御された入力でReactのフォームを作ります。テキスト欄、textarea、select、チェックボックス、ラジオボタン、多くの項目を1つのstateオブジェクトで扱う方法、onSubmitでの送信、バリデーションエラーの表示、React 19のフォームアクションを学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

Reactのフォームは、各項目の値をstateに保持します。入力欄は value={state} を表示し、onChange でそれを更新します。ユーザーが送信すると、onSubmit ハンドラが e.preventDefault() を呼んでページの再読み込みを止め、すでにstateにある値を使います。

名前を入力してEnterを押すか、ボタンをクリックしてください。次に e.preventDefault(); の行を削除してみてください。プレビューには、ネイティブの送信でブラウザが送るはずだった項目が表示されます。本物のページなら、ページの再読み込みも起きるところです。

値がstateから来る入力欄を、制御された入力(controlled input)と呼びます。もう1つの方法、つまりDOMに値を持たせて後で読む方法は、制御コンポーネントと非制御コンポーネントで扱っています。

テキスト入力とtextarea

どのテキスト欄も同じように動きます。value はstateから、onChange は e.target.value を読みます。<textarea> も同じパターンです。HTMLではテキストをタグの間に置きますが、Reactでは value として渡します。

値を自分で設定するので、ユーザーが入力したものを表示前に変えられます。タイトル欄はすべての文字を大文字にします。文字数のカウンターはキーを押すたびに更新されます。body がstateで、変更のたびに再レンダリングされるからです。

select

<select> は、optionの selected ではなく、select要素自身で value を受け取ります。e.target.value は選ばれたoptionの value です。

複数選択にするには multiple を付け、value に配列を渡します。選択肢は Array.from(e.target.selectedOptions, (o) => o.value) で読みます。

チェックボックスとラジオボタン

チェックボックスは value ではなく checked で制御し、そのハンドラは true か false である e.target.checked を読みます。グループ内のラジオボタンは name を共有し、stateが自分の値と等しいときにチェックされます。

チェックボックスで e.target.value を読むのはよくある間違いです。value 属性のないチェックボックスでは、チェックが入っていてもいなくても文字列 "on" が返ります。

多くの項目を1つのstateオブジェクトで扱う

10項目あるフォームに、10回の useState 呼び出しと10個のハンドラは必要ありません。1つのオブジェクトを持ち、各入力欄にキーと一致する name を付け、その名前を計算されたキー [e.target.name] として使うハンドラを1つ書きます。

ハンドラは入力の種類を確認するので、チェックボックスは真偽値を、それ以外はテキストを保存します。項目を追加するには、最初のオブジェクトにキーを追加し、同じ name を持つ入力欄を追加するだけで、ハンドラは変更不要です。スプレッドがほかのすべての項目を保ちます(配列とオブジェクトの更新を参照)。

フォームを送信する

送信のロジックは、ボタンの onClick ではなく、フォームの onSubmit に置いてください。onSubmit はユーザーがテキスト欄でEnterを押したときにも発生しますが、クリックハンドラではそれを取りこぼします。フォームの中の <button> はデフォルトで送信ボタンなので、ほかのボタンには type="button" を付けて送信しないようにします。

コンソールを開き、パスワードを入力してEnterを押してください。フォームがオブジェクト全体を記録します。「Clear」をクリックしても何も記録されません。type="button" だからです。その属性を削除すると、Clearも送信するようになります。

バリデーションとエラーの表示

単純なバリデーションは、値を受け取ってメッセージのオブジェクトを返す関数です。送信時に実行し、結果をstateに保存して、各メッセージを対応する項目の下に表示します。リクエストの実行中は送信ボタンを無効にし、ダブルクリックでフォームが2回送られないようにします。

項目が空のままSaveをクリックすると両方のメッセージが表示されるので、それから入力してください。saveProfile は本物のリクエストの代わりで、800ミリ秒待ってから解決します。noValidate 属性はブラウザ独自のバリデーションの吹き出しを無効にし、自分のメッセージだけが表示されるようにします。これを外して入力欄に required を付け、比べてみてください。

エラーは保存せずにレンダリング中に計算することもできます。たとえば const emailError = form.email && !form.email.includes('@') とすれば、ユーザーの入力中にメッセージが表示されます。フォームに合うタイミングを選んでください。送信時のほうが落ち着いていて、入力中のほうがフィードバックが速くなります。

React 19のフォームアクション

React 19では、フォームを扱う方法がもう1つ追加されました。フォームの action propに関数を渡します。Reactがページの再読み込みを防ぎ、name を持つすべての項目を含む FormData オブジェクトを渡してその関数を呼びます。項目にstateはまったく必要ありません。非制御の入力欄で、アクションが終わるとReactがクリアします。

自分のコメントを入力して投稿してください。リストには入力したテキストが表示され、入力欄は Nice docs に戻ります。アクションが解決した後、Reactが非制御の入力欄を defaultValue にリセットするからです。アクションは非同期にでき、実行中、Reactはその更新をトランジションとして扱います。

アクションの保留中の状態やエラーを表示するには、useActionStateで包みます。これは最新の結果、フォーム用に包んだアクション、isPending フラグを返します。フォームの奥深くにあるボタンも、react-dom の useFormStatus で保留中の状態を読めます。

import { useFormStatus } from 'react-dom';

function SubmitButton() {
    const { pending } = useFormStatus();
    return <button disabled={pending}>{pending ? 'Posting...' : 'Post'}</button>;
}

ここのエディタでは useFormStatus が読み込まれていないので、この静的なブロックでだけ示しています。これはフォームを描画するコンポーネントではなく、<form> の中で描画されるコンポーネントから呼ぶ必要があります。

onSubmit を使う制御されたstateも、FormData を使うフォームアクションも、React 19ではどちらも使えます。制御された項目は、入力中に反応するフォーム(リアルタイムのバリデーション、文字数カウンター、互いに依存する項目)に向いています。アクションは、ユーザーが送信したときだけ値が必要なフォームに向いています。

よくある質問

Reactで入力欄の値を取得するには?

stateに保存し、onChange から更新します:<input value={name} onChange={e => setName(e.target.value)} />。これで現在のテキストは常に name に入っています。チェックボックスでは e.target.value ではなく e.target.checked を読みます。

Reactでフォームがページを再読み込みしないようにするには?

onSubmit ハンドラの最初で e.preventDefault() を呼びます。これがないと、ブラウザは通常のフォーム送信を行い、ページを移動して再読み込みします。

Reactで多くの入力欄を1つのstateで扱うには?

stateに1つのオブジェクトを持ち、各入力欄にキーと一致する name を付け、計算されたキーを使う1つのハンドラを使います:setForm({ ...form, [e.target.name]: e.target.value })。

Reactのtextareaがchildrenではなくvalueを使うのはなぜですか?

HTMLではtextareaのテキストはタグの間に置きますが、Reactではinputと同じように value(または defaultValue)として渡します。そうすることで、Reactのフォームのすべてのテキスト欄が同じように動きます。

React 19のフォームアクションとは何ですか?

React 19では、<form action={fn}> に関数を渡せます。Reactはページの再読み込みを防ぎ、フォームの FormData を渡してその関数を呼び、終わると非制御の入力欄をリセットします。useActionState はその上に保留中フラグと結果を加えます。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める