この課題のねらい
ここでは「フォーム入力をちゃんと React で扱う」ことがテーマです。
キーワードは、state・onChange・onSubmit・バリデーション(入力チェック)。
最終的に、「ユーザーが入力 → state に反映 → 送信ボタンで処理 → 入力がおかしければエラー表示」という一連の流れを、自分で組み立てられるようになるのがゴールです。
フォーム入力をstate管理する
単一入力フォームの基本パターン
まずは、名前を入力するだけの超シンプルなフォームから始めます。
ポイントは「input の値を state で管理する(=制御されたコンポーネントにする)」ことです。
"use client";
import { useState } from "react";
export default function SimpleFormPage() {
const [name, setName] = useState("");
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setName(event.target.value);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>フォーム入力の基本</h1>
<form>
<label>
名前:
<input
type="text"
value={name}
onChange={handleChange}
style={{ marginLeft: "8px" }}
/>
</label>
<p style={{ marginTop: "12px" }}>今入力されている名前:{name}</p>
</form>
</main>
);
}
TSXここで大事なのは次の関係です。
input の value に state(name)を渡しているので、「表示されている値の正体は state」。
onChange で入力イベントを受け取り、event.target.value を setName に渡して state を更新している。
ユーザーがタイプするたびに state が更新され、その値がまた input に反映される、というループになっている。
これが「フォーム入力を state 管理する」の基本形です。
この形をベースに、項目を増やしたり、送信処理を足したりしていきます。
submit処理を実装する
フォーム送信時に処理を走らせる
次は、「送信ボタンを押したときに何かする」パターンです。
React では form に onSubmit を付けて、イベントの中で処理を書きます。
"use client";
import { useState } from "react";
export default function SubmitFormPage() {
const [name, setName] = useState("");
const [submittedName, setSubmittedName] = useState<string | null>(null);
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setName(event.target.value);
}
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault(); // ブラウザのデフォルト送信(ページリロード)を止める
setSubmittedName(name);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>フォーム送信の例</h1>
<form onSubmit={handleSubmit}>
<label>
名前:
<input
type="text"
value={name}
onChange={handleChange}
style={{ marginLeft: "8px" }}
/>
</label>
<button
type="submit"
style={{
marginLeft: "12px",
padding: "4px 12px",
borderRadius: "9999px",
border: "none",
backgroundColor: "#3b82f6",
color: "white",
cursor: "pointer",
}}
>
送信
</button>
</form>
{submittedName && (
<p style={{ marginTop: "16px" }}>送信された名前:{submittedName}</p>
)}
</main>
);
}
TSXここでの流れを整理します。
ユーザーが入力するたびに name state が更新される。
送信ボタンを押すと form の onSubmit が発火し、handleSubmit が呼ばれる。
handleSubmit の中で event.preventDefault() を呼び、ブラウザの標準のフォーム送信(ページリロード)を止める。
その上で、今の name を submittedName にコピーし、「送信された値」として画面に表示している。
「入力中の値」と「送信済みの値」を分けて state 管理しているのがポイントです。
実務でも、「入力中のフォーム」と「サーバーに送った値」を分けて扱うことはよくあります。
入力チェック(バリデーション)を追加する
まずは「空文字禁止」のシンプルなチェック
挑戦課題は「入力チェック追加」です。
最初はシンプルに、「名前が空のまま送信しようとしたらエラーにする」パターンから始めましょう。
"use client";
import { useState } from "react";
export default function ValidatedFormPage() {
const [name, setName] = useState("");
const [submittedName, setSubmittedName] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setName(event.target.value);
if (error) {
setError(null); // 入力し直したらエラーを消す
}
}
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
if (name.trim() === "") {
setError("名前を入力してください。");
setSubmittedName(null);
return;
}
setSubmittedName(name.trim());
setError(null);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>入力チェック付きフォーム</h1>
<form onSubmit={handleSubmit}>
<label>
名前:
<input
type="text"
value={name}
onChange={handleChange}
style={{ marginLeft: "8px" }}
/>
</label>
<button
type="submit"
style={{
marginLeft: "12px",
padding: "4px 12px",
borderRadius: "9999px",
border: "none",
backgroundColor: "#3b82f6",
color: "white",
cursor: "pointer",
}}
>
送信
</button>
</form>
{error && (
<p style={{ color: "red", marginTop: "8px" }}>{error}</p>
)}
{submittedName && !error && (
<p style={{ marginTop: "12px" }}>送信された名前:{submittedName}</p>
)}
</main>
);
}
TSXここでの重要ポイントはこうです。
error という state を用意し、「エラーメッセージを入れる場所」を作っている。
handleSubmit の中で name.trim() が空かどうかをチェックし、空なら error にメッセージをセットして return している(送信処理を中断)。
エラーがあるときは赤文字でメッセージを表示し、エラーがないときだけ送信結果を表示している。
handleChange の中で「入力し直したらエラーを消す」ことで、ユーザーが修正したときにちゃんとエラーが消えるようにしている。
これだけで、「雑に送信させない」「何がダメかを教える」という最低限の UX が成立します。
もう一歩:複数項目のフォームをイメージする
ここまで理解できたら、次は「項目が増えたとき」をイメージしてみてください。
例えば、「名前」と「メールアドレス」の 2 項目フォームなら、こんな感じになります。
state を name と email の 2 つにする。
エラーも nameError と emailError に分けるか、オブジェクトでまとめる。
handleSubmit の中で、それぞれの項目に対してチェックを行い、どこがダメかを個別に表示する。
実務では、これをもう少し抽象化して「フォーム用のカスタムフック」や「フォームライブラリ(react-hook-form など)」を使いますが、
まずは「state で入力を持つ」「onChange で更新する」「onSubmit でチェックして処理する」という素の流れを自分の手で書けることが大事です。
まとめ:フォームでつかんでほしいこと
この課題で押さえてほしいのは次の感覚です。
input の value を state に結びつけて、「表示されている値の正体は state」にする。
onChange で入力イベントを受け取り、event.target.value を使って state を更新する。
form の onSubmit で event.preventDefault() を呼び、React 側で送信処理をコントロールする。
送信前に入力チェックを行い、ダメなときはエラーメッセージを state で管理して画面に表示する。
ここが腑に落ちると、ログインフォーム、問い合わせフォーム、プロフィール編集画面など、
「ユーザーが何かを入力して送信する画面」を自分の頭で設計できるようになっていきます。
