有没有办法在ZOD中使用跨字段验证来检查其他字段然后渲染条件

问题描述 投票:0回答:1

我正在使用 React Typescript 构建带有 Zod 验证模式的表单。

我想要实现什么? 因此,我想根据另一个字段的值呈现条件。每个国家/地区都有自己的电话代码和电话号码格式。每次有人选择电话号码时,我希望电话号码字段具有不同的验证规则。这是我到目前为止所拥有的。这基本上是我实际项目的复制。

import { Formik, Field, Form, FormikHelpers } from 'formik'
import { toFormikValidationSchema } from 'zod-formik-adapter'
import { z } from 'zod'
import './App.css'


const FormField = z.object({
  firstName: z.string().max(6, 'Too long'),
  lastName: z.string(),
  email: z.string(),
  phoneCode: z.string(),
  phoneNumber: z.string()
})

const Register = FormField.required()
.refine((input) => {
  return input.phoneCode === '0044';
}, 'Phone code must be 0044');


Register.parse({
  firstName: 'fewfwf',
  lastName: 'Doe',
  email: '[email protected]',
  phoneCode: '0044',
  phoneNumber: '0044'
})

type Values = z.infer<typeof Register>

function App() {

  return (
    <>
      <Formik
        initialValues={{
          firstName: '',
          lastName: '',
          email: '',
          phoneCode: '',
          phoneNumber: ''
        }}
        onSubmit={(
          values: Values,
          { setSubmitting }: FormikHelpers<Values>
        ) => {
          setTimeout(() => {
            alert(JSON.stringify(values, null, 2));
            setSubmitting(false);
          }, 500);
        }}
        validationSchema={toFormikValidationSchema(FormField)}
      >
        {({ errors, touched }) => (
          <Form>
             {errors.firstName ? (<div>{errors.firstName}</div>) : null}
            <label htmlFor="firstName">First Name</label>
            <Field id="firstName" name="firstName" placeholder="John" />

            <label htmlFor="lastName">Last Name</label>
            <Field id="lastName" name="lastName" placeholder="Doe" />

            <label htmlFor="email">Email</label>
            <Field
              id="email"
              name="email"
              placeholder="[email protected]"
              type="email"
            />

            <label htmlFor="phoneCode">PhoneCode</label>
            <Field id="phoneCode" name="phoneCode" placeholder="+44" />

            {errors.phoneNumber && touched.phoneNumber ? (<div>{errors.phoneNumber}</div>) : null}
            <label htmlFor="phoneNumber">PhoneNumber</label>
            <Field id="phoneNumber" name="phoneNumber" placeholder="789434556" />

            <button type="submit">Submit</button>
          </Form>
        )}
      </Formik>

    </>
  )
}

export default App

如您所见,我使用了 .refine() 并添加了一些随机条件来测试。当我通过解析方法运行它时,它会正确验证并工作,但是,当我从前端提交表单时,它仍然会执行,即使phoneCode不等于“0044”。这是 .refine 方法的正常行为吗?还有其他更实用的方法吗?

reactjs typescript formik zod
1个回答
0
投票

Zod 中的

.refine()
方法用于在解析阶段应用自定义细化检查。它确保输入满足指定的条件,如果不满足则抛出错误。但是,当您同时使用 Formik 和 Zod 时,您可能希望以不同的方式处理这些动态验证规则。

在您的情况下,您希望根据所选的电话代码值有条件地验证电话号码字段。为此,您可以对验证模式中的各个字段使用细化方法,并在细化函数内执行条件检查。但是,请记住,细化函数是在解析阶段评估的,并且不会自动应用到 Formik 上下文中

import { Formik, Field, Form, FormikHelpers } from 'formik';
import { toFormikValidationSchema } from 'zod-formik-adapter';
import { z } from 'zod';
import './App.css';

const FormField = z.object({
  firstName: z.string().max(6, 'Too long'),
  lastName: z.string(),
  email: z.string(),
  phoneCode: z.string(),
  phoneNumber: z
    .string()
    .refine((value, data) => {
      // Perform your dynamic validation based on phoneCode
      return data.phoneCode === '0044' ? value.length === 10 : true;
    }, 'Invalid phone number length'),
});

type Values = z.infer<typeof FormField>;

function App() {
  return (
    <>
      <Formik
        initialValues={{
          firstName: '',
          lastName: '',
          email: '',
          phoneCode: '',
          phoneNumber: '',
        }}
        onSubmit={(
          values: Values,
          { setSubmitting }: FormikHelpers<Values>,
        ) => {
          setTimeout(() => {
            alert(JSON.stringify(values, null, 2));
            setSubmitting(false);
          }, 500);
        }}
        validationSchema={toFormikValidationSchema(FormField)}
      >
        {({ errors, touched }) => (
          <Form>
            {errors.firstName ? <div>{errors.firstName}</div> : null}
            <label htmlFor="firstName">First Name</label>
            <Field id="firstName" name="firstName" placeholder="John" />

            <label htmlFor="lastName">Last Name</label>
            <Field id="lastName" name="lastName" placeholder="Doe" />

            <label htmlFor="email">Email</label>
            <Field
              id="email"
              name="email"
              placeholder="[email protected]"
              type="email"
            />

            <label htmlFor="phoneCode">PhoneCode</label>
            <Field id="phoneCode" name="phoneCode" placeholder="+44" />

            {errors.phoneNumber && touched.phoneNumber ? (
              <div>{errors.phoneNumber}</div>
            ) : null}
            <label htmlFor="phoneNumber">PhoneNumber</label>
            <Field
              id="phoneNumber"
              name="phoneNumber"
              placeholder="789434556"
            />

            <button type="submit">Submit</button>
          </Form>
        )}
      </Formik>
    </>
  );
}

export default App;

这样就对phoneNumber字段使用了

.refine()
方法,根据phoneCode进行动态验证。根据您的具体验证逻辑调整细化函数内的条件。

这样,验证将在解析阶段应用,如果不满足条件,您将看到错误消息。

© www.soinside.com 2019 - 2024. All rights reserved.