NextJS、Zod 和 React Hook Form - 受控和非受控组件

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

我正在使用 NextJS 14、Zod 和 ShadCN UI,它使用 React-Hook-Form。

我正在创建一个可用于“创建”和“更新”的表单。我将值传递到表单中,并使用“创建”表单的那些或空字符串设置 defaultValues。但是,由于某些字段是强制性的,因此空字符串仍然是一个值,并且会绕过正常表单字段验证。在文本输入上,我可以添加 .min(1, {message: 'some message'}) 但我还有一个下拉菜单,我正在努力强制执行强制值。理想情况下,我可以保持 ZOD 模式干净,无需添加 .optional(),并且表单将为我管理必要的字段。

最近我决定将必填字段的默认值设置为“未定义”,这解决了我的必填字段问题,但是当将数据输入到这些字段之一时,我收到一条错误消息,让我知道表单正在从不受控制转变为受控制- 由于“未定义”作为起始值。

Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen.

实现这一目标并且仍然具有必填字段的最佳方法是什么?

--- ZOD 架构 ---

    export const AddressFormSchema = z.object({
  form_type: z.string().optional(),
  id: z.string().optional(),
  type: z.string(),
  line1: z.string().trim().toLowerCase(),
  line2: z.string().trim().toLowerCase().optional(),
  city: z.string().trim().toLowerCase().optional(),
  state: z.string().optional(),
  postal_code: z
    .union([
      z.string().length(0, {
        message: "No more than 10 digits",
      }),
      z.string().trim().max(10),
    ])
    .optional(),
  agency_id: z.string().optional(),
  contact_id: z.string().optional(),
  supplier_id: z.string().optional(),
  created_at: z.string().optional(),
  updated_at: z.string().optional(),
});

-表格页---

    const { update } = props;

  const defaultValues = {
    form_type: update ? "update" : "create",
    id: update ? props.address?.id : undefined,
    type: update ? props.address?.type : undefined,
    line1: update ? props.address?.line1 : undefined,
    line2: update ? props.address?.line2 || "" : "",
    city: update ? props.address?.city || "" : "",
    state: update ? props.address?.state || "" : "",
    postal_code: update ? props.address?.postal_code || "" : "",
  };

  const form = useForm<AddressFormSchemaType>({
    resolver: zodResolver(AddressFormSchema),
    defaultValues: defaultValues,
  });
next.js react-hook-form zod shadcnui
1个回答
0
投票

可用于

create
update
的表单可以有条件地呈现特定于操作的表单字段,并且还可以添加自定义错误。

为了实现此目的,您可以使用

discriminatedUnion
merge

这是一个示例架构

// common fields for "create" and "update"
const BaseAddressSchema = z.object({
  street: z.string().min(3).max(255),
  city: z.string().min(3).max(255),
  state: z.string().min(3).max(255),
});

const AddressSchema = z.discriminatedUnion("form_type", [
  z
    .object({
      form_type: z.literal("create"),
      supplier_id: z.string().min(3).max(255), // validation only applied when "create" value selected in dropdown
    })
    .merge(BaseAddressSchema),
  z
    .object({
      form_type: z.literal("update"),
     // here you can add "update" specific fields
    })
    .merge(BaseAddressSchema),
]);

// in order to add validation to the dropdown eg(no default value present and value must be selected) you can add here
const CustomAddressSchema = z
  .object({
    form_type: z.string().min(1, "Form Type is required"),
  })
  .and(AddressSchema);

在组件中你可以这样

  const {
    register,
    watch,
    handleSubmit,
    formState: { errors },
  } = useForm<any>({
    resolver: zodResolver(CustomAddressSchema ),
    defaultValues: defaultValues,
  });
  const form_type_value = watch("form_type");

...
// conditionally render a certain field based on the dropdown

      {form_type_value == "create" && (
        <div>
          <label className="mr-2">Supplier id</label>
          <input
            className="border-[1px] border-black"
            type="text"
            {...register("supplier_id")}
            placeholder="supplier Id"
          />
          {/* @ts-ignore */}
          <p>{errors.supplier_id && errors?.supplier_id?.message}</p>
        </div>
      )}

这是一个工作复制品https://replit.com/@MubashirWaheed/zodDiscrimulatedUnion#app/page.tsx

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