💎
React-Hook-Form + Zodでよくあるパスワードバリデーションを実装する
目標
パスワード設定画面によくある画面のバリデーションをReact-Hook-Form
(以下 rfc), Zod
で実装します
実装する項目は以下の通りです
- 現在のパスワードと新しいパスワードが一致しているか
- 新しいパスワードと確認用のパスワードが一致しているか
rfc
とzod
を利用した基本的なフォームの実装法を載せてあります!
導入についてはこちらをご覧ください
結論
.refine
または.superRefine
を使いましょう!
const schema = z
.object({
currentPassword: z.string().min(1, 'パスワードを入力してください'),
newPassword: z
.string()
.min(8, 'パスワードは8文字以上で入力してください')
.regex(
/^(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,100}$/i,
'パスワードは半角英数字混合で入力してください'
),
newPasswordConfirm: z
.string()
.min(1, '確認用のパスワードを入力してください'),
})
.superRefine(({ currentPassword, newPassword, newPasswordConfirm }, ctx) => {
if (newPassword !== newPasswordConfirm) {
ctx.addIssue({
path: ['newPasswordConfirm'],
code: 'custom',
message: 'パスワードが一致しません',
});
}
if (currentPassword === newPassword) {
ctx.addIssue({
path: ['newPassword'],
code: 'custom',
message: '現在のパスワードと同じです',
});
}
});
一度スキーマを作成し、メソッドの.superRefine
で高度な処理を実装します。
rfc のwatch
で値を取得してバリデーションという方法と比較して
- バリデーションロジックを一箇所にまとめられる
- 余計な変数を定義する必要がなくなる
といったコードの可読性を上げることができます。
解説
.superRefine(({ currentPassword, newPassword, newPasswordConfirm }, ctx) => {
// 処理
})
superRefine
の中の関数にはval
, ctx
が渡されます。
val
はz.object
で定義したフィールドの値がオブジェクト形式で渡されるので、必要なものを分割代入で取得しています。
一方ctx
の型は下記のようになっています
types.ts
export type RefinementCtx = {
addIssue: (arg: IssueData) => void;
path: (string | number)[];
};
addIssue
という関数と、各フィールドへのパスの配列path
が含まれています。
このうち、addIssue
を利用することにより、高度なバリデーションを実装します。
新しいパスワード と 確認用パスワード が一致しない場合
if (newPassword !== newPasswordConfirm) {
ctx.addIssue({
path: ['newPasswordConfirm'],
code: 'custom',
message: 'パスワードが一致しません',
})
}
-
path
:どのフィールドのバリデーションとして追加するか -
code
: エラーコード(custom 以外もある) -
message
: エラーメッセージ
他にも、バリデーションを早期中止するためのフラグfatal
などを引数で渡せますが、詳しくはドキュメントを参照してください
現在のパスワード と 新しいパスワード が一致した場合
if (currentPassword === newPassword) {
ctx.addIssue({
path: ['newPassword'],
code: 'custom',
message: '現在のパスワードと同じです',
})
あとはこれを rfc のリゾルバーに渡せば、いつものようにバリデーションが効きます
type FormValues = z.infer<typeof schema>
...
const {
register,
...
} = useForm<FormValues>({
+ resolver: zodResolver(schema),
})
終わりに
ここまで閲覧いただきありがとうございます!
zod の公式ドキュメントをあまり読んでいなかったので、こういった書き方があると知った時は感動しました。。
よければ ❤️ やコメントをしていただけると嬉しいです! (ご指摘のコメントもお待ちしております)
Discussion
superRefine
勉強になりました。記事書いてくださってありがとうございます。記事記載の内容を少しアレンジしてデモを作成してみました。
簡単ですが、以上です。