モダンJavaScriptの基礎から始める挫折しないためのReact入門 60日コース | フォーム・コンポーネント設計 - DAY 31:フォーム基礎

React 60日で身につけるReact
スポンサーリンク
スポンサーリンク
  1. DAY 31:フォーム基礎 前半 ― 「input+value+onChange」でフォームの土台を作る
  2. フォームの入口:inputタグをReactで使う
    1. まずは「ただのinput」を置いてみる
  3. valueとonChange ― 「Reactが入力を支配する」仕組み
    1. Stateとvalueをつなぐ
    2. onChangeで「入力された瞬間にStateを更新する」
  4. Controlled Component(制御されたコンポーネント)とは何か
    1. 「Reactが値を完全に管理している入力欄」のこと
    2. なぜControlled Componentが重要なのか
  5. 例題:シンプルな「自己紹介フォーム」を作る
    1. 名前+自己紹介をControlled Componentで管理する
  6. Controlled Componentの「型」を体に染み込ませる
    1. どの入力欄でも使える基本形
  7. 練習用テンプレート:DAY 31 前半版「フォーム基礎コンポーネント」
  8. DAY 31 前半のまとめ
  9. DAY 31:フォーム基礎 中盤 ― 「input+value+onChange」を設計目線で理解する
  10. Controlled Componentを「フォームの標準形」として捉える
    1. Reactがフォームを支配するとはどういうことか
    2. Uncontrolled Componentとの違いをイメージする
  11. 複数項目フォームをControlled Componentで設計する
    1. 名前+メール+年齢の3項目フォームを考える
  12. Controlled Componentだからできる「リアルタイムな制御」
    1. 入力内容に応じてボタンを有効/無効にする
  13. 「input+value+onChange」の設計パターンを言語化する
    1. どの入力欄でも通用する考え方
  14. 練習用テンプレート:DAY 31 中盤版「ログインフォーム」
  15. DAY 31 中盤のまとめ
  16. DAY 31:フォーム基礎 後半 ― 「Controlled Component」を自分の標準フォームにする
  17. Controlled Componentを「フォームの当たり前」にする
    1. もう一度、基本形をはっきりさせる
  18. Controlled Componentだからできる「入力制御」を体験する
    1. 例:最大文字数を制限する
  19. エラー表示と組み合わせて「フォームらしさ」を出す
    1. 例:メールアドレスの簡単なチェック
  20. 小さなフォームを「送信まで」つなげてみる
    1. 例:問い合わせフォームのミニ版
  21. 「input+value+onChange」を自分のフォームテンプレートにする
    1. どんなフォームでもまずこの形から始める
  22. DAY 31 後半のまとめ ― 「Controlled Component」をフォームの軸にする

DAY 31:フォーム基礎 前半 ― 「input+value+onChange」でフォームの土台を作る

DAY 31では、Reactにおけるフォームの一番基本となる考え方、 input・value・onChange・Controlled Component(制御されたコンポーネント) を学んでいきます。

ここで身につける感覚は、 この先の「ログインフォーム」「プロフィール編集」「問い合わせフォーム」など、 あらゆるフォームの土台になりますので、じっくり進めていきます。

フォームの入口:inputタグをReactで使う

まずは「ただのinput」を置いてみる

HTMLでは、テキスト入力欄は次のように書きます。

<input type="text" />

Reactでも基本は同じですが、 フォームをしっかり扱うためには「Stateとセット」で考える必要があります。

まずは、何も制御していないシンプルなコンポーネントを見てみます。

function SimpleInput() {
  return (
    <div>
      <h2>フォーム基礎(前半)</h2>
      <input type="text" />
    </div>
  );
}

export default SimpleInput;
JSX

この状態では、ブラウザが入力内容を勝手に管理していて、 React側は「何が入力されているか」を知りません。

ここから一歩進んで、 Reactが入力内容を把握し、コントロールできる状態を作っていきます。

valueとonChange ― 「Reactが入力を支配する」仕組み

Stateとvalueをつなぐ

Reactでフォームを扱うときの基本は、

入力欄の中身をStateで管理する

という考え方です。

まず、入力内容を保存するためのStateを用意します。

import { useState } from "react";

function NameInput() {
  const [name, setName] = useState("");

  return (
    <div>
      <h2>フォーム基礎(前半)</h2>
      <input type="text" />
    </div>
  );
}

export default NameInput;
JSX

次に、この nameinputvalue に渡します。

<input type="text" value={name} />
JSX

これで、

  • 入力欄の表示内容は、常にState name の値になります。

ただし、このままだとユーザーが入力しても値は変わりません。 なぜなら、name を更新していないからです。

onChangeで「入力された瞬間にStateを更新する」

そこで登場するのが onChange です。

<input
  type="text"
  value={name}
  onChange={(e) => setName(e.target.value)}
/>
JSX

ここで起きていることをステップで説明します。

  1. ユーザーが入力欄に文字を打つ
  2. onChange イベントが発生し、 (e) => setName(e.target.value) が呼ばれる
  3. e.target.value には「今入力された文字列」が入っている
  4. setName(...) によってState name が更新される
  5. Reactが再レンダリングし、value={name} によって入力欄の表示が新しい値になる

この流れによって、

入力欄の中身は、常にStateと同期した状態

になります。

ここが、Reactフォームの一番重要なポイントです。

Controlled Component(制御されたコンポーネント)とは何か

「Reactが値を完全に管理している入力欄」のこと

今の NameInput コンポーネントは、 Controlled Component(制御されたコンポーネント) の典型例です。

function NameInput() {
  const [name, setName] = useState("");

  return (
    <div>
      <h2>フォーム基礎(前半)</h2>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <p>現在の名前:{name}</p>
    </div>
  );
}
JSX

「制御されたコンポーネント」とは、

入力欄の値を、ReactのStateが完全に管理しているコンポーネント

のことです。

具体的には、

  • value にStateを渡している
  • onChange で入力のたびにStateを更新している

この2つが揃っていると、 その入力欄は「Controlled Component」と呼ばれます。

なぜControlled Componentが重要なのか

Controlled Componentにすると、次のようなメリットがあります。

  • 入力内容をいつでもStateから参照できる → 送信ボタンを押したときに、Stateからまとめて値を取り出せる
  • 入力値に制限をかけやすい → 例えば「数字だけ許可」「最大文字数を制限」などを、onChange 内で制御できる
  • バリデーション(入力チェック)をしやすい → 「空文字ならエラー」「メール形式でなければエラー」などをStateと組み合わせて管理できる
  • 画面の他の部分と連動させやすい → 入力内容に応じてボタンの有効/無効を切り替えるなどが簡単になる

Reactで本格的なフォームを作るときは、 ほぼ必ずこの「Controlled Component」の考え方が土台になります。

例題:シンプルな「自己紹介フォーム」を作る

名前+自己紹介をControlled Componentで管理する

フォーム基礎の前半では、 シンプルな自己紹介フォームを題材にして、 inputtextareavalueonChange・Controlled Componentをまとめて体験してみます。

import { useState } from "react";

function ProfileFormBasic() {
  const [name, setName] = useState("");
  const [bio, setBio] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault(); // ページリロードを防ぐ
    alert(`送信された自己紹介\n名前: ${name}\n自己紹介: ${bio}`);
  };

  return (
    <div>
      <h2>フォーム基礎(前半)― 自己紹介フォーム</h2>

      <form onSubmit={handleSubmit}>
        <div>
          <label>
            名前:
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="名前を入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            自己紹介:
            <textarea
              value={bio}
              onChange={(e) => setBio(e.target.value)}
              placeholder="簡単な自己紹介を書いてください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <button type="submit">送信</button>
        </div>
      </form>

      <hr />

      <div>
        <h3>プレビュー</h3>
        <p>名前:{name}</p>
        <p>自己紹介:{bio}</p>
      </div>
    </div>
  );
}

export default ProfileFormBasic;
JSX

ここでのポイントを整理します。

  • namebio は、どちらもStateで管理しています。
  • inputtextareavalue に、それぞれ namebio を渡しています。
  • onChange で、入力のたびにStateを更新しています。
  • formonSubmit で、Stateから値を取り出してアラート表示しています。

このフォーム全体が、 「Controlled Componentの集合」 になっています。

Controlled Componentの「型」を体に染み込ませる

どの入力欄でも使える基本形

フォーム基礎の前半で、 ぜひ自分の中に刻んでほしい「型」があります。

それは、次のような形です。

const [value, setValue] = useState("");

<input
  type="text"
  value={value}
  onChange={(e) => setValue(e.target.value)}
/>
JSX

この型は、

  • メールアドレス入力欄
  • パスワード入力欄
  • 検索ボックス
  • タイトル入力欄

など、あらゆるテキスト入力でそのまま使えます。

さらに、少し応用するとこうなります。

const [age, setAge] = useState("");

<input
  type="number"
  value={age}
  onChange={(e) => {
    const v = e.target.value;
    // 数字以外を弾く、最大値を制限するなどのロジックもここに書ける
    setAge(v);
  }}
/>
JSX

重要なポイント:

Controlled Componentの基本形は、 「State+value+onChange」の3点セット。 これをそのまま型として覚えておくと、フォーム作りが一気に楽になります。

練習用テンプレート:DAY 31 前半版「フォーム基礎コンポーネント」

最後に、DAY 31前半の内容をすべて含んだ 練習用テンプレートをまとめます。

import { useState } from "react";

function FormBasicDay31() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`送信された情報\n名前: ${name}\nメール: ${email}`);
  };

  return (
    <div>
      <h2>フォーム基礎(DAY 31 前半)</h2>

      <form onSubmit={handleSubmit}>
        <div>
          <label>
            名前:
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="名前を入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            メール:
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="メールアドレスを入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <button type="submit">送信</button>
        </div>
      </form>

      <hr />

      <div>
        <h3>現在の入力内容</h3>
        <p>名前:{name}</p>
        <p>メール:{email}</p>
      </div>
    </div>
  );
}

export default FormBasicDay31;
JSX

DAY 31 前半のまとめ

DAY 31前半で、自分の中に定着させてほしいのは次の4つです。

  • input は、ReactでもHTMLと同じタグですが、 本格的に使うときは必ずStateとセットで考えること。
  • value にStateを渡すことで、 「入力欄の中身は常にStateと同期した状態」になること。
  • onChange で入力のたびにStateを更新することで、 Reactが入力内容を完全に把握できるようになること。
  • Controlled Component は、 「value+onChangeでStateに完全に紐づいた入力欄」のことであり、 すべてのフォームの土台になる考え方であること。

この「State+value+onChange」の型が身につくと、 この先のフォーム設計(バリデーション・複数項目・エラー表示など)が、 ぐっと理解しやすく、実装しやすくなっていきます。


DAY 31:フォーム基礎 中盤 ― 「input+value+onChange」を設計目線で理解する

DAY 31の中盤では、前半で触れた input・value・onChange・Controlled Component を、 「ただ書ける」から一歩進んで、設計目線で理解することを目標にします。

ここで身につけたいのは、

  • なぜ value と onChange をセットで書くのか
  • Controlled Component にすると何が嬉しいのか
  • 複数項目のフォームをどう設計するのか

という、フォーム設計の「考え方」です。

Controlled Componentを「フォームの標準形」として捉える

Reactがフォームを支配するとはどういうことか

前半で作ったシンプルな例を、もう一度振り返ります。

import { useState } from "react";

function NameInput() {
  const [name, setName] = useState("");

  return (
    <div>
      <h2>フォーム基礎(中盤)</h2>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <p>現在の名前:{name}</p>
    </div>
  );
}

export default NameInput;
JSX

このコンポーネントでは、

  • 入力欄の中身は value={name} によってState name に完全に紐づいています。
  • ユーザーが入力するたびに onChange が呼ばれ、setName でStateが更新されます。

つまり、

「入力欄の値は、ReactのStateが唯一の真実(Single Source of Truth)になっている」

という状態です。

これが Controlled Component の本質です。

Uncontrolled Componentとの違いをイメージする

逆に、valueonChange を使わずに、 ただ <input type="text" /> と書いた場合はどうでしょうか。

この場合、

  • 入力内容はブラウザが内部的に管理していて、Reactはその値を直接は知りません。
  • 値を取り出したいときは、ref を使ってDOMから読み取る必要があります。

これは Uncontrolled Component(制御されていないコンポーネント) に近い状態です。

中盤では、

「Reactでフォームをしっかり作るなら、基本はControlled Componentを使う」

という方針を、自分の中で決めてしまうのがおすすめです。

複数項目フォームをControlled Componentで設計する

名前+メール+年齢の3項目フォームを考える

中盤では、単一の入力欄ではなく、 複数項目を持つフォームをControlled Componentで設計してみます。

例として、次の3項目を持つフォームを考えます。

  • 名前(text)
  • メール(email)
  • 年齢(number)

まずはStateを用意します。

import { useState } from "react";

function ProfileForm() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [age, setAge] = useState("");

  return (
    <div>
      <h2>フォーム基礎(中盤)― プロフィールフォーム</h2>
      {/* ここにフォームを作っていきます */}
    </div>
  );
}

export default ProfileForm;
JSX

次に、それぞれのStateを valueonChange に紐づけます。

<form>
  <div>
    <label>
      名前:
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="名前を入力してください"
      />
    </label>
  </div>

  <div style={{ marginTop: "8px" }}>
    <label>
      メール:
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="メールアドレスを入力してください"
      />
    </label>
  </div>

  <div style={{ marginTop: "8px" }}>
    <label>
      年齢:
      <input
        type="number"
        value={age}
        onChange={(e) => setAge(e.target.value)}
        placeholder="年齢を入力してください"
      />
    </label>
  </div>
</form>
JSX

ここで重要なのは、

  • 各入力欄が、それぞれ専用のStateに紐づいていること
  • すべての入力欄が「Controlled Component」になっていること

です。

この形が、「複数項目フォームの基本形」になります。

Controlled Componentだからできる「リアルタイムな制御」

入力内容に応じてボタンを有効/無効にする

Controlled Componentの強みは、 入力内容に応じてUIをリアルタイムに制御できることです。

例えば、「名前とメールが両方入力されていないと送信ボタンを押せない」 という仕様を簡単に実現できます。

const isValid = name.trim() !== "" && email.trim() !== "";

<button type="submit" disabled={!isValid}>
  送信
</button>
JSX

フォーム全体のコードはこうなります。

import { useState } from "react";

function ProfileForm() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [age, setAge] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`送信された情報\n名前: ${name}\nメール: ${email}\n年齢: ${age}`);
  };

  const isValid = name.trim() !== "" && email.trim() !== "";

  return (
    <div>
      <h2>フォーム基礎(中盤)― プロフィールフォーム</h2>

      <form onSubmit={handleSubmit}>
        <div>
          <label>
            名前:
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="名前を入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            メール:
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="メールアドレスを入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            年齢:
            <input
              type="number"
              value={age}
              onChange={(e) => setAge(e.target.value)}
              placeholder="年齢を入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <button type="submit" disabled={!isValid}>
            送信
          </button>
        </div>
      </form>

      <hr />

      <div>
        <h3>現在の入力内容</h3>
        <p>名前:{name}</p>
        <p>メール:{email}</p>
        <p>年齢:{age}</p>
        <p>送信可能か:{isValid ? "はい" : "いいえ"}</p>
      </div>
    </div>
  );
}

export default ProfileForm;
JSX

ここで起きていることは、

  • isValid が、State nameemail に基づいて計算されている
  • disabled={!isValid} によって、入力状況に応じてボタンの有効/無効が切り替わる

という流れです。

これが、Controlled Componentの大きなメリットです。

「input+value+onChange」の設計パターンを言語化する

どの入力欄でも通用する考え方

中盤では、次のような「設計パターン」を自分の中に持ってほしいです。

  1. まずStateを決める
    • 「この入力欄は何を表すのか?」を考え、 それに対応するStateを用意します。
    jsxconst [username, setUsername] = useState("");
  2. valueにStateを渡すjsx<input type="text" value={username} />
    • 入力欄の表示内容は、常にStateに紐づきます。
  3. onChangeでStateを更新するjsx<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
    • ユーザーの入力に応じて、Stateが更新されます。
  4. 必要に応じて制御ロジックを追加するjsxonChange={(e) => { const v = e.target.value; if (v.length <= 20) { setUsername(v); } }}
    • 最大文字数を制限するなどのロジックを、onChange 内に書けます。

この4ステップが、「Controlled Componentを設計する基本パターン」です。

練習用テンプレート:DAY 31 中盤版「ログインフォーム」

最後に、DAY 31中盤の内容をまとめた シンプルなログインフォームのテンプレートを示します。

import { useState } from "react";

function LoginFormBasic() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`ログイン試行\nメール: ${email}\nパスワード: ${password}`);
  };

  const isValid =
    email.trim() !== "" &&
    password.trim() !== "" &&
    password.length >= 6;

  return (
    <div>
      <h2>フォーム基礎(DAY 31 中盤)― ログインフォーム</h2>

      <form onSubmit={handleSubmit}>
        <div>
          <label>
            メール:
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="メールアドレスを入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            パスワード:
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              placeholder="パスワードを入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <button type="submit" disabled={!isValid}>
            ログイン
          </button>
        </div>
      </form>

      <hr />

      <div>
        <h3>現在の入力状態</h3>
        <p>メール:{email}</p>
        <p>パスワード:{password ? "入力済み" : "未入力"}</p>
        <p>ログイン可能か:{isValid ? "はい" : "いいえ"}</p>
      </div>
    </div>
  );
}

export default LoginFormBasic;
JSX

DAY 31 中盤のまとめ

DAY 31中盤で、自分の中に定着させてほしいのは次のポイントです。

  • Controlled Component は、「ReactのStateが唯一の真実として入力値を管理するコンポーネント」であること。
  • input+value+onChange の3点セットが、フォーム設計の基本形であること。
  • 複数項目フォームでも、各入力欄をそれぞれStateに紐づけることで、 入力内容を完全に把握・制御できること。
  • Controlled Componentだからこそ、 「入力内容に応じてボタンを無効化する」「リアルタイムにバリデーションする」 といった高度なフォーム挙動を自然に実装できること。

この設計目線が身につくと、 この先のフォーム・コンポーネント設計(エラー表示、複雑なバリデーション、ネストしたフォームなど)が、 ぐっと理解しやすく、実装しやすくなっていきます。


DAY 31:フォーム基礎 後半 ― 「Controlled Component」を自分の標準フォームにする

DAY 31の後半では、 これまで学んできた input・value・onChange・Controlled Component を、 「なんとなく書ける」から 「自分の標準フォームパターン」 にまで引き上げていきます。

ここで目指すのは、

  • どんなフォームでもまずこの形で考えられるようになること
  • バリデーションやエラー表示の土台を作れるようになること
  • 小さなフォームでも「設計されたフォーム」として扱えるようになること

です。

Controlled Componentを「フォームの当たり前」にする

もう一度、基本形をはっきりさせる

まず、Controlled Componentの基本形を改めて確認します。

import { useState } from "react";

function BasicInput() {
  const [value, setValue] = useState("");

  return (
    <div>
      <h2>フォーム基礎(後半)</h2>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <p>現在の入力:{value}</p>
    </div>
  );
}

export default BasicInput;
JSX

このコンポーネントでは、

  • value にStateを渡している
  • onChange で入力のたびにStateを更新している

ので、完全に Controlled Component になっています。

後半では、この形を「特別なもの」ではなく、 「フォームを書くときの当たり前のスタート地点」 として捉えてください。

Controlled Componentだからできる「入力制御」を体験する

例:最大文字数を制限する

Controlled Componentの強みは、 入力された瞬間にロジックを挟めることです。

例えば、「ユーザー名は最大20文字まで」という制限を入れてみます。

import { useState } from "react";

function UsernameInput() {
  const [username, setUsername] = useState("");

  const handleChange = (e) => {
    const v = e.target.value;
    if (v.length <= 20) {
      setUsername(v);
    }
  };

  return (
    <div>
      <h2>ユーザー名入力(最大20文字)</h2>
      <input
        type="text"
        value={username}
        onChange={handleChange}
        placeholder="ユーザー名を入力してください(最大20文字)"
      />
      <p>現在のユーザー名:{username}</p>
      <p>文字数:{username.length} / 20</p>
    </div>
  );
}

export default UsernameInput;
JSX

ここで起きていることは、

  • ユーザーが入力するたびに handleChange が呼ばれる
  • v.length <= 20 のときだけ setUsername(v) している
  • 21文字目以降はStateが更新されないので、入力欄にも反映されない

という流れです。

ポイント:

Controlled Componentでは、「入力された瞬間にチェックして、通すかどうかを決める」ことができます。 これは、ブラウザ任せのUncontrolledな入力ではやりにくい制御です。

エラー表示と組み合わせて「フォームらしさ」を出す

例:メールアドレスの簡単なチェック

次に、メールアドレス入力欄に簡単なエラー表示をつけてみます。

import { useState } from "react";

function EmailInputWithError() {
  const [email, setEmail] = useState("");

  const isEmpty = email.trim() === "";
  const isInvalidFormat = !isEmpty && !email.includes("@");

  return (
    <div>
      <h2>メール入力(簡易バリデーション)</h2>

      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="メールアドレスを入力してください"
      />

      {isEmpty && <p style={{ color: "red" }}>メールアドレスを入力してください。</p>}
      {isInvalidFormat && (
        <p style={{ color: "red" }}>メールアドレスの形式が正しくありません。</p>
      )}

      <p>現在のメール:{email}</p>
    </div>
  );
}

export default EmailInputWithError;
JSX

ここでのポイントは、

  • 入力値 email をStateで管理している
  • isEmptyisInvalidFormat を、Stateに基づいて計算している
  • 条件に応じてエラーメッセージを表示している

というところです。

重要な感覚:

Controlled Componentでは、「入力値 → 状態判定 →エラー表示」という流れを、 すべてReactの中で完結させることができます。

これが、フォームを「ただの入力欄」から「ちゃんと設計されたUI」に変えていく第一歩です。

小さなフォームを「送信まで」つなげてみる

例:問い合わせフォームのミニ版

DAY 31後半では、 小さな問い合わせフォームを題材に、 input・value・onChange・Controlled Componentを「送信まで」つなげてみます。

import { useState } from "react";

function ContactFormMini() {
  const [email, setEmail] = useState("");
  const [message, setMessage] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();

    const trimmedEmail = email.trim();
    const trimmedMessage = message.trim();

    if (trimmedEmail === "" || trimmedMessage === "") {
      alert("メールとメッセージの両方を入力してください。");
      return;
    }

    alert(`問い合わせ送信\nメール: ${trimmedEmail}\nメッセージ: ${trimmedMessage}`);

    setEmail("");
    setMessage("");
  };

  const isValid =
    email.trim() !== "" &&
    message.trim() !== "" &&
    message.trim().length >= 10;

  return (
    <div>
      <h2>問い合わせフォーム(ミニ版)</h2>

      <form onSubmit={handleSubmit}>
        <div>
          <label>
            メール:
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="メールアドレスを入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            メッセージ:
            <textarea
              value={message}
              onChange={(e) => setMessage(e.target.value)}
              placeholder="10文字以上で入力してください"
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <button type="submit" disabled={!isValid}>
            送信
          </button>
        </div>
      </form>

      <hr />

      <div>
        <h3>現在の入力状態</h3>
        <p>メール:{email}</p>
        <p>メッセージ:{message}</p>
        <p>送信可能か:{isValid ? "はい" : "いいえ"}</p>
      </div>
    </div>
  );
}

export default ContactFormMini;
JSX

ここで起きていることを整理します。

  • emailmessage をStateで管理している
  • どちらも value+onChange でControlled Componentになっている
  • isValid をStateに基づいて計算し、ボタンの disabled に反映している
  • handleSubmitpreventDefault() を呼び、ページリロードを防いでいる
  • 送信後にStateをリセットして、フォームを初期状態に戻している

ここまで来ると、「Reactでフォームを作る一連の流れ」がほぼ見えています。

「input+value+onChange」を自分のフォームテンプレートにする

どんなフォームでもまずこの形から始める

DAY 31後半のゴールは、 次のようなテンプレートを「自分のフォームの出発点」として持つことです。

import { useState } from "react";

function MyFormTemplate() {
  const [field1, setField1] = useState("");
  const [field2, setField2] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    // field1, field2 を使って何かする
  };

  const isValid = field1.trim() !== "" && field2.trim() !== "";

  return (
    <div>
      <h2>フォームテンプレート</h2>

      <form onSubmit={handleSubmit}>
        <div>
          <label>
            フィールド1:
            <input
              type="text"
              value={field1}
              onChange={(e) => setField1(e.target.value)}
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <label>
            フィールド2:
            <input
              type="text"
              value={field2}
              onChange={(e) => setField2(e.target.value)}
            />
          </label>
        </div>

        <div style={{ marginTop: "8px" }}>
          <button type="submit" disabled={!isValid}>
            送信
          </button>
        </div>
      </form>
    </div>
  );
}

export default MyFormTemplate;
JSX

このテンプレートから、

  • フィールド名を変える
  • 型(text, email, password, number, textarea)を変える
  • バリデーションロジックを追加する
  • 送信処理をAPI呼び出しに変える

などをしていけば、 かなり多くのフォームが組み立てられるようになります。

DAY 31 後半のまとめ ― 「Controlled Component」をフォームの軸にする

DAY 31後半で、自分の中にしっかり残してほしいのは次のポイントです。

  • input+value+onChange の3点セットが、Reactフォームの基本形であり、 それを使った入力欄が Controlled Component であること。
  • Controlled Componentでは、 入力された瞬間にロジックを挟めるため、 最大文字数制限・形式チェック・エラー表示・ボタンの有効/無効などを自然に実装できること。
  • 小さなフォームでも、 「Stateで値を管理する → valueで表示する → onChangeで更新する → submitでまとめて扱う」 という流れを意識すると、設計されたフォームになること。
  • 自分用のフォームテンプレートとして、 useStatevalueonChangeonSubmitisValid のセットを持っておくと、 どんなフォームでも迷わず書き始められること。

この「Controlled Componentを軸にしたフォーム設計」が身につくと、 この先のフォーム・コンポーネント設計(複雑な入力、ネストしたフォーム、エラー管理、API連携など)が、 ぐっと現実的で、楽しく組み立てられるようになっていきます。

タイトルとURLをコピーしました