react-hook-formでメールアドレスやパスワードの再入力時の一致確認バリデーションを実装する方法
久しぶりの記事更新になります。
最近reactを触ることが多くなってきたので、その中でformの実装でお世話になっている「react-hook-form」のお話しになります。
今回はその「react-hook-form」でメールアドレスやパスワードなどの再入力項目のバリデーションの実装方法を紹介します。
目次
まずはこちらのデモを御覧ください。
codesandboxでサンプルを用意しました。
実際に入力してバリデーションの動作確認をしてみてください。
使用した主なライブラリとそのバージョン
今回の記事を書くにあたって検証したバージョンになります。
- react 18.0.0
- next.js 12.1.4
- typescript 4.6.3
- react-hook-form 7.29.0
- @hookform/error-message 2.0.0
実装したコードの全体
以下が今回実装したコードの全てになります。
styleなど不要なコードがありますので、重要な部分のみを後述します。
import styles from "../styles/Home.module.scss";
// react-hook-form
import { useForm, SubmitHandler } from "react-hook-form"; // SubmitHandlerは、submitイベントに関する関数の型宣言に使う
import { ErrorMessage } from "@hookform/error-message"; // エラーメッセージコンポーネント
type FormData = {
email: string;
email_confirmation: string;
};
const Form = () => {
// useFormの設定&使用したい機能を呼び出す
const {
register,
handleSubmit,
formState: { errors },
getValues,
trigger
} = useForm<FormData>({
mode: "onBlur",
criteriaMode: "all"
});
// submit時の処理
const onSubmit: SubmitHandler<FormData> = async (data) => {
console.log(data);
alert("OK. メールアドレスは一致しています。");
};
return (
<div className={styles["wrapper"]}>
<form id="form" onSubmit={handleSubmit(onSubmit)}>
<p>メールアドレスを入力してください。</p>
{/* メールアドレスを入力 */}
<div className={styles["form-input"]}>
<input
type="email"
placeholder="sample@a.a"
{...register("email", {
required: "メールアドレスを入力してください。",
onBlur: () => {
if (getValues("email_confirmation")) {
trigger("email_confirmation");
}
},
pattern: {
value: /^[\w\-._]+@[\w\-._]+\.[A-Za-z]+/,
message: "入力形式がメールアドレスではありません。"
}
})}
/>
</div>
{/* メールアドレスを再入力 */}
<div className={styles["form-input"]}>
<input
type="email"
placeholder="確認のためメールアドレスを再入力"
{...register("email_confirmation", {
required: "確認のためメールアドレスを再入力してください。",
validate: (value) => {
return (
value === getValues("email") || "メールアドレスが一致しません"
);
}
})}
/>
</div>
{/* メールアドレスのバリデーションメッセージ */}
<ErrorMessage
errors={errors}
name="email"
render={({ message }) =>
message ? (
<p className={styles["form-validateMessage"]}>{message}</p>
) : null
}
/>
{/* メールアドレス再入力のバリデーションメッセージ */}
<ErrorMessage
errors={errors}
name="email_confirmation"
render={({ message }) =>
message ? (
<p className={styles["form-validateMessage"]}>{message}</p>
) : null
}
/>
<button type="submit" className={styles["form-submit-button"]}>
<span>送信する</span>
</button>
</form>
</div>
);
};
export default Form;
解説
それでは先程のコードの解説をしたいと思います。
react-hook-form関連をインポート
import { useForm, SubmitHandler } from "react-hook-form";
import { ErrorMessage } from "@hookform/error-message";
これは解説するまでものありませんが、react-hook-formとバリデーションメッセージを表示する@hookform/error-messageをインポートします。
useFormを使い初期設定をする
const {
register,
handleSubmit,
formState: { errors },
getValues,
trigger
} = useForm<FormData>({
mode: "onBlur",
criteriaMode: "all"
});
このあたりはreact-hook-formを使う上で必須なことなので細かいことは説明しませんが、今回の再入力時の一致確認バリデーションで重要なのものはgetValues
とtrigger
です。
バリデーションの実装
それではバリデーションの実装についてお話します。これがこの記事の本編です(笑)
バリデーションの部分を抜き出したのが以下のコードになります。※不要divやclassNameなどは削除しております。
<input
type="email"
placeholder="sample@a.a"
{...register("email", {
required: "メールアドレスを入力してください。",
onBlur: () => {
if (getValues("email_confirmation")) {
trigger("email_confirmation");
}
},
pattern: {
value: /^[\w\-._]+@[\w\-._]+\.[A-Za-z]+/,
message: "入力形式がメールアドレスではありません。"
}
})}
/>
<input
type="email"
placeholder="確認のためメールアドレスを再入力"
{...register("email_confirmation", {
required: "確認のためメールアドレスを再入力してください。",
validate: (value) => {
return (
value === getValues("email") || "メールアドレスが一致しません"
);
}
})}
/>
<ErrorMessage
errors={errors}
name="email"
render={({ message }) =>
message ? (
<p>{message}</p>
) : null
}
/>
<ErrorMessage
errors={errors}
name="email_confirmation"
render={({ message }) =>
message ? (
<p>{message}</p>
) : null
}
/>
再入力部分
まずは再入力部分のバリデーションから解説します。
<input
type="email"
placeholder="確認のためメールアドレスを再入力"
{...register("email_confirmation", {
required: "確認のためメールアドレスを再入力してください。",
validate: (value) => {
return (
value === getValues("email") || "メールアドレスが一致しません"
);
}
})}
/>
react-hook-formはregister
にオブジェクトを渡して色々バリデーションを設定できるのですが、そのなかでvalidate
を使用します。validate
は任意のバリデーションを設定できます。
validate: (value) => {
return (
value === getValues("email") || "メールアドレスが一致しません"
);
}
validate
に上記のコードのように設定すると「通常入力項目」と「再入力項目」の比較が行えます。
validate
の引数のvalue
は再入力項目の値が入っています。
getValues
は第一引数に指定したname属性をもつ要素のvalueを取得します。これにより「通常入力項目」の値を取得しています。
通常入力部分
通常入力部分について解説します。
<input
type="email"
placeholder="sample@a.a"
{...register("email", {
required: "メールアドレスを入力してください。",
onBlur: () => {
if (getValues("email_confirmation")) {
trigger("email_confirmation");
}
},
pattern: {
value: /^[\w\-._]+@[\w\-._]+\.[A-Za-z]+/,
message: "入力形式がメールアドレスではありません。"
}
})}
/>
ここで重要なのはonBlur
の箇所です。
onBlur: () => {
if (getValues("email_confirmation")) {
trigger("email_confirmation");
}
},
なにをやっているのかというと
入力項目からフォーカスが外れた時にgetValues
で再入力項目の値を取得し、値がある場合にtrigger
を使い再入力項目のバリデーションを実行します。
なぜこのような処理が必要かというと、再入力項目のバリデーション設定だけでは2つの項目の入力を完了した後に通常入力項目を変更した際に値が一致しなくなった場合バリデーションが動かないからです。
なのでtrigger
で再入力項目のバリデーションも同時に実行してあげる必要があります。
バリデーションメッセージ
これはインポートした@hookform/error-messageを普通に使用しているだけです。
下記のようにnameにinputで設定したnameを渡すだけでバリデーションメッセージが表示されます。
<ErrorMessage
errors={errors}
name="email"
render={({ message }) =>
message ? (
<p>{message}</p>
) : null
}
/>
最後に
個人的にreact-hook-formはvalidate
で今回のような再入力バリデーションも簡単に実装できるのでかなり気に入ってます。
また、今回はなるべく分かりやすくするため各種inputなどコンポーネント化などはしていないので、使用する場合はそれぞれをコンポーネント化するなり好き使用してください。