Zod スキーマ / conformZodMessage

conformZodMessage

検証動作を制御するためのカスタムメッセージのセットです。これは、フィールドの一つに対して非同期検証が必要な場合に便利です。

#オプション

conformZodMessage.VALIDATION_SKIPPED

このメッセージは、検証がスキップされ、 Conform が前回の結果を代わりに使用すべきであることを示すために使用されます。

conformZodMessage.VALIDATION_UNDEFINED

このメッセージは、検証が定義されていないことを示し、 Conform がサーバー検証にフォールバックすべきであることを示すために使用されます。

#

以下は、メールアドレスがユニークであるかを検証するサインアップフォームの例です。

1import type { Intent } from '@conform-to/react';
2import { useForm } from '@conform-to/react';
3import { parseWithZod, conformZodMessage } from '@conform-to/zod';
4import { z } from 'zod';
5
6  // スキーマを共有する代わりに、スキーマを作成する関数 `createSchema` を準備します。
7  // `intent` は `parseWithZod` ヘルパーによって提供されます。
8  intent: Intent | null,
9  options?: {
10    isEmailUnique: (email: string) => Promise<boolean>;
11  },
12) {
13  return z
14    .object({
15      email: z
16        .string()
17        .email()
18        // スキーマをパイプして、メールアドレスが有効な場合にのみ実行されるようにします。
19        .pipe(
20          z.string().superRefine((email, ctx) => {
21            const isValidatingEmail =
22              intent === null ||
23              (intent.type === 'validate' && intent.payload.name === 'email');
24
25            if (!isValidatingEmail) {
26              ctx.addIssue({
27                code: 'custom',
28                message: conformZodMessage.VALIDATION_SKIPPED,
29              });
30              return;
31            }
32
33            if (typeof options?.isEmailUnique !== 'function') {
34              ctx.addIssue({
35                code: 'custom',
36                message: conformZodMessage.VALIDATION_UNDEFINED,
37                fatal: true,
38              });
39              return;
40            }
41
42            return options.isEmailUnique(email).then((isUnique) => {
43              if (!isUnique) {
44                ctx.addIssue({
45                  code: 'custom',
46                  message: 'Email is already used',
47                });
48              }
49            });
50          }),
51        ),
52    })
53    .and(
54      z
55        .object({
56          password: z.string({ required_error: 'Password is required' }),
57          confirmPassword: z.string({
58            required_error: 'Confirm password is required',
59          }),
60        })
61        .refine((data) => data.password === data.confirmPassword, {
62          message: 'Password does not match',
63          path: ['confirmPassword'],
64        }),
65    );
66}
67
68export async function action({ request }) {
69  const formData = await request.formData();
70  const submission = await parseWithZod(formData, {
71    schema: (intent) =>
72      // インテントに基づいてzodスキーマを作成します。
73      createSchema(intent, {
74        isEmailUnique(email) {
75          // データベースをクエリ
76        },
77      }),
78    async: true,
79  });
80
81  if (submission.status !== 'success') {
82    return submission.reply();
83  }
84
85  // ...
86}
87
88export default function Signup() {
89  const lastResult = useActionData();
90  const [form, fields] = useForm({
91    lastResult,
92    onValidate({ formData }) {
93      return parseWithZod(formData, {
94        // `isEmailUnique` が定義されていない状態でスキーマを作成します。
95        schema: (intent) => createSchema(intent),
96      });
97    },
98  });
99
100  // ...
101}