🏎️

ほぼJSなしで完璧なReactフォームをつくる

2024/03/06に公開

gist

import { ChangeEvent, FormEvent } from "react";

export default function Form() {
  const showError = (message: string) => (e: FormEvent<HTMLInputElement>) => {
    if (e.currentTarget.validity.valueMissing) {
      e.currentTarget.setCustomValidity(message);
    } else if (
      e.currentTarget.validity.patternMismatch &&
      e.currentTarget.name === "postcode"
    ) {
      e.currentTarget.setCustomValidity("郵便番号正しく入力してね");
    } else if (
      e.currentTarget.validity.tooShort &&
      e.currentTarget.name === "phoneNumber"
    ) {
      e.currentTarget.setCustomValidity("電話番号正しく入力してね");
    } else {
      e.currentTarget.setCustomValidity("");
    }
  };
  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    e.target.setCustomValidity("");
  };
  return (
    <form>
      <section
        style={{
          display: "flex",
          flexDirection: "column",
          gap: "10px",
        }}
      >
        <label htmlFor="firstName">名前</label>
        <input
          id="firstName"
          name="firstName"
          required
          autoComplete="given-name"
          onInvalid={showError("名前を入力してね")}
          onChange={handleInputChange}
        />
        <label htmlFor="lastName">苗字</label>
        <input
          id="lastName"
          name="lastName"
          required
          autoComplete="family-name"
          onInvalid={showError("苗字を入力してね")}
          onChange={handleInputChange}
        />
        <label htmlFor="phoneNumber">電話番号</label>
        <input
          id="phoneNumber"
          name="phoneNumber"
          type="text"
          inputMode="numeric"
          pattern="[0-9]*"
          minLength={11}
          autoComplete="tel"
          onInvalid={showError("電話番号を入力してね")}
          onChange={handleInputChange}
        />
        <label htmlFor="postcode">郵便番号</label>
        <input
          id="postcode"
          type="text"
          name="postcode"
          inputMode="numeric"
          required
          pattern="[0-9]{7}"
          onInvalid={showError("郵便番号を入力してね")}
          onChange={handleInputChange}
        />
        <label htmlFor="address">住所</label>
        <input
          id="address"
          type="text"
          name="address"
          required
          autoComplete="address-level1"
          onInvalid={showError("住所を入力してね")}
          onChange={handleInputChange}
        />
        <label htmlFor="building">建物名</label>
        <input
          id="building"
          type="text"
          name="building"
          autoComplete="address-level2"
          required
          onInvalid={showError("建物名を入力してね")}
          onChange={handleInputChange}
        />
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="email"
          name="email"
          required
          inputmode="email"
          autoComplete="email"
          onInvalid={showError("Emailを入力してね")}
          onChange={handleInputChange}
        />
        <button type="submit">Submit</button>
      </section>
    </form>
  );
}

把握すべき属性

required

論理属性の required 属性は、存在する場合、所有するフォームを送信する前にユーザーが入力に値を指定しなければならないことを示します。

submit時、未入力の場合怒られるようになる

autoComplete

HTML の autocomplete 属性は、ウェブ開発者は入力欄にどの種類の情報が期待されているかをブラウザーに示唆することができるのに加え、ユーザーエージェント(ブラウザー)がフォーム入力欄の値を埋めるための自動支援を提供する必要があることを指定することができます。

ブラウザーが過去に該当する入力値を補完してくれるよ。アクセシビリティ的にも必須

type

<input> は HTML の要素で、ユーザーからデータを受け取るための、ウェブベースのフォーム用の操作可能なコントロールを作成するために使用します。

入力値の制御によく使われるよ。ex: 数値に制限する時、type="number"にするなど。
また入力補完によって入る値も制御できる。ex: type="email"の場合、メールが補完される

inputmode

inputmode はグローバル属性で、ユーザーが要素やその内容を編集する際に入力されるデータの型のヒントとなる列挙型属性です。 これにより、ブラウザーは適切な仮想キーボードを表示することができます。

ようは、ユーザーがデータ入力フィールドにフォーカスを当てた時に、特定の種類のデータ入力に最適化されたキーボードを表示させるようになる。

ex: iOSでは、numericの場合、テンキーが表示される。

pattern

フォームコントロールの値が一致すべき正規表現を指定します

正規表現を使って、入力値の制御できる!
上記のコードでは以下なような使いかたしてるよ

      <input
          id="postcode"
          type="text"
          name="postcode"
          inputMode="numeric"
          required
          pattern="[0-9]{7}" //ここ 👈👈👈
          onInvalid={showError("郵便番号を入力してね")}
          onChange={handleInputChange}
        />

入力値として郵便番号を想定しているので、7桁に制限している

minLength/maxLength

ユーザーが <input> または <textarea> に入力できる最大/最小文字数

    <input
          id="phoneNumber"
          name="phoneNumber"
          type="text"
          inputMode="numeric"
          pattern="[0-9]*"
          minLength={11} //ここ 👈👈👈
          autoComplete="tel"
          onInvalid={showError("電話番号を入力してね")}
          onChange={handleInputChange}
        />

電話番号においてはminLengthを指定。必須ではないので(requiredの定義がなし)、未入力かもしくは11桁の値を入れる必要ある

ユーザー操作にフィードバック

ここの部分

const showError = (message: string) => (e: FormEvent<HTMLInputElement>) => {
    if (e.currentTarget.validity.valueMissing) {
      e.currentTarget.setCustomValidity(message);
    } else if (
      e.currentTarget.validity.patternMismatch &&
      e.currentTarget.name === "postcode"
    ) {
      e.currentTarget.setCustomValidity("郵便番号正しく入力してね");
    } else if (
      e.currentTarget.validity.tooShort &&
      e.currentTarget.name === "phoneNumber"
    ) {
      e.currentTarget.setCustomValidity("電話番号正しく入力してね");
    } else {
      e.currentTarget.setCustomValidity("");
    }
  };
  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    e.target.setCustomValidity("");
  };

validity

フォームバリデーションイベントのステートを返すプロパティ

inputやtext-areaなどの入力値の検証基準。
これらのプロパティを利用して、フォームの検証フィードバックを提供したりする。

setCustomValidity

setCustomValidity() は HTMLObjectElement インターフェイスのメソッドで、この要素に独自の検証メッセージを設定します。


任意のエラーメッセージを設定できるようしてくれる

参考

https://developer.mozilla.org/ja/docs/Learn/Forms/Form_validation

Discussion