1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zodの omit で登録・編集フォームのバリデーションを分ける

Posted at

はじめに

react-hook-formZod を組み合わせたフォーム実装を行った際、登録時と更新時でバリデーションが必要な項目が異なるケースがありました。
この記事では、そこでハマったポイントと解決策をまとめます。

問題の箇所

「新規登録画面」と「編集画面」で、共通のフォームを使っていました。
ただし、登録時はユーザーID(user_id)の入力が必要ですが、編集時はユーザーIDを表示するだけで、編集できないようにしたいと考えていました。

最初は、新規登録用に作成した以下のスキーマを編集画面でも使い回そうとしました。

export const userFormSchema = z.object({
  user_id: z
    .string()
    .nonempty('IDは必須です')
    .min(3, 'IDは3文字以上で入力してください')
    .regex(/^[a-zA-Z]+$/, 'IDは英単語のみ使用できます'),
  name: z
    .string()
    .nonempty('名前は必須です')
    .max(50, '名前は50文字以内で入力してください'),
  // ...他のフィールド
});

export type UserFormSchemaType = z.infer<typeof userFormSchema>;

ところが、編集画面では user_id フィールドをフォームに含めておらず、表示専用のため、react-hook-formhandleSubmit() 実行時に user_id のバリデーションでエラーとなり、フォームが送信できませんでした。

問題内容

  • バリデーションエラーでフォームが送信できない
  • handleSubmit()のコールバックが呼ばれない

解決方法

Zodのomitメソッドを活用する

調査の結果、Zodには便利なomitメソッドがあることが分かりました。
これを使えば、既存のスキーマから特定のフィールドだけを除外したスキーマを簡単に作ることができます。

// 登録用スキーマ(user_idを含む)
export const userFormSchema = z.object({
  user_id: z
    .string()
    .nonempty('IDは必須です')
    .min(3, 'IDは3文字以上で入力してください')
    .regex(/^[a-zA-Z]+$/, 'IDは英単語のみ使用できます'),
  name: z
    .string()
    .nonempty('名前は必須です')
    .max(50, '名前は50文字以内で入力してください'),
  // ...他のフィールド
});

// 🔵編集用スキーマ(user_idを除外)
export const userEditFormSchema = userFormSchema.omit({ user_id: true });

export type UserFormSchemaType = z.infer<typeof userFormSchema>;
export type UserEditFormSchemaType = z.infer<typeof userEditFormSchema>;

編集コンポーネントでは、userEditFormSchemaUserEditFormSchemaTypeを使用します。

const {
  register,
  handleSubmit,
  control,
  setValue,
  formState: { errors },
} = useForm<UserEditFormSchemaType>({
  resolver: zodResolver(userEditFormSchema),
  defaultValues: {
    // user_idを削除
    name: '',
    description: '',
    // ...他のデフォルト値
  },
  mode: 'onChange',
});

// ...省略...

// 更新処理
const onUpdateUser = async (data: UserEditFormSchemaType) => {
  // ここでuser_idを追加
  const formData = {
    ...data,
    user_id: user?.user_id || '', 
    // ...他のデータ加工
  };

  // DBへの更新処理
  const result = await updateUser(formData);
  // ...省略...
};

このように omit を使えば、共通したバリデーションルールを再定義する必要がなくなる上、バリデーションのルールを変更する時も1箇所で対応できます。

おわりに

フォームの一部のフィールドが異なるために、バリデーションスキーマを別で定義することは冗長のため避けたかったです。
そこでZodomitを活用すれば、スキーマを再利用できてスッキリした構成になるのでおすすめです。
他にもおすすめの対応方法があれば、是非教えてください!

参考サイト

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?