Next.jsのselect要素のバリデーション

2024/11/30に公開

select要素のバリデーションとは

ブラウザのコンソールでユーザーがoptionタグののvalueをいじってフォーム送信すると、意図した選択肢以外の値が送られてきてしまいます。
これを防ぐために、別ファイルであらかじめ選択肢を配列で羅列しておき、それをもとにArray.includes()関数でチェックします。

手順

  1. 別ファイルに選択肢を羅列した配列を作る。
  2. Array.includes()関数で、あらかじめ作成した配列に含まれているかチェック

↓react-hook-formを使わない場合

// 1. 別ファイルに選択肢を羅列した配列を作る。
// options.js
export const validCategories = [
  "tech",     // 技術
  "design",   // デザイン
  "marketing" // マーケティング
];
// 2. Array.includes()関数で、あらかじめ作成した配列に含まれているかチェック
// MyForm.tsx
import React, { useState } from "react";
import { validCategories } from "./options"; // 選択肢をインポート

const MyForm = () => {
  const [category, setCategory] = useState<string>("");
  const [error, setError] = useState<string>("");

  // フォーム送信時の処理
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();

    // 選択肢が無効な場合はエラー
    if (!validCategories.includes(category)) {
      setError("無効なカテゴリが選択されました。正しいカテゴリを選択してください。");
      return; // フォーム送信を中止
    }

    // エラーがなければ送信処理を実行
    setError("");
    console.log("フォーム送信データ:", { category });
    // ここでデータ送信処理を行う
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="category">カテゴリ</label>
        <select
          id="category"
          value={category}
          onChange={(e) => setCategory(e.target.value)}
        >
           // valueの値が選択肢と同じときは、map関数で展開してもいいですね。
          <option value="">選択してください</option>
          <option value="tech">技術</option>
          <option value="design">デザイン</option>
          <option value="marketing">マーケティング</option>
        </select>
        {/* エラーメッセージ */}
        {error && <p style={{ color: 'red' }}>{error}</p>}
      </div>

      <button type="submit">送信</button>
    </form>
  );
};

export default MyForm;

↓react-hook-formを使った場合

// 1. 別ファイルに選択肢を羅列した配列を作る。
// options.js
export const validCategories = [
  "tech",     // 技術
  "design",   // デザイン
  "marketing" // マーケティング
];
// 2. Array.includes()関数で、あらかじめ作成した配列に含まれているかチェック
// MyForm.tsx
import React from "react";
import { useForm } from "react-hook-form"; // react-hook-form をインポート
import { validCategories } from "./options"; // 選択肢をインポート

type FormData = {
  category: string;
};

const MyForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>(); // useForm を使ってフォームを管理

  // フォーム送信時の処理
  const onSubmit = (data: FormData) => {
    console.log("フォーム送信データ:", data);

    // バリデーションチェック
    if (!validCategories.includes(data.category)) {
      alert("無効なカテゴリが選択されました。");
      return;
    }

    // 正常な場合は送信処理を行う
    alert("送信成功!");
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="category">カテゴリ</label>
        <select
          id="category"
          {...register("category", {
            required: "カテゴリを選択してください",
            validate: (value) => validCategories.includes(value) || "無効なカテゴリです",
          })}
        >
          // valueの値が選択肢と同じときは、map関数で展開してもいいですね。
          <option value="">選択してください</option>
          <option value="tech">技術</option>
          <option value="design">デザイン</option>
          <option value="marketing">マーケティング</option>
        </select>

        {/* エラーメッセージ */}
        {errors.category && <p style={{ color: "red" }}>{errors.category.message}</p>}
      </div>

      <button type="submit">送信</button>
    </form>
  );
};

export default MyForm;

Discussion