使用 React Hook Form 和 Zod(和 Shad UI)创建字段

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

在我的表单中,我希望用户能够添加来自其他平台的不确定数量的用户(在本例中,其他平台是 Go(棋盘游戏)服务器):

A sketch of the form

我的 Zod 模式此时看起来像这样:

export const profileFormValidationSchema = z.object({
  go_users: z.record(
    z.string(),
    z.object({
      server: z.string().optional(),
      username: z.string().optional(),
      strength: z.string().optional(),
    }),
  ).optional(),
})

我的表单有点复杂,但是代码是开源的,你可以查看这里。这是我现在正在做的一个简化示例:

type ProfileFormProps = {
  initialValues?: ProfileFormValidation
}

export function ProfileForm({ initialValues }: ProfileFormProps) {
  const profileForm = useForm<ProfileFormValidation>({
    resolver: zodResolver(profileFormValidationSchema),
    defaultValues: initialValues,
  })

  const [totalUsers, setTotalUsers] = useState(
    Object.values(initialValues?.go_users ?? {}).length,
  )

  return (
    <>
      <h2 className="mt-6">Edite Seu Perfil</h2>

      <Form {...profileForm}>
        <form
          onSubmit={profileForm.handleSubmit(onSubmit)}
          className="flex flex-col gap-6"
        >
          <fieldset className="grid grid-cols-12 gap-x-2 gap-y-3 items-end">
            <legend className="ml-3 mb-2 text-lg font-bold col-span-2">
              3. Usuários em Servidores de Go
            </legend>

            {Array.from(Array(totalUsers + 1), (e, i) => {
              const key = `user${i}`
              return (
                <>
                  <FormItem className="col-span-3">
                    <FormLabel className="ml-3">
                      Servidor - {i}
                    </FormLabel>
                    <FormControl>
                      <Input
                        placeholder="OGS"
                        value={
                          profileForm.getValues(
                            "go_users",
                          )?.[key]?.server ?? ""
                        }
                        onChange={(e) => {
                          const currentUsers =
                            profileForm.getValues(
                              "go_users",
                            )
                          const newGoUsers = {
                            ...currentUsers,
                          }
                          newGoUsers[key] = {
                            ...currentUsers?.user1,
                            server: e.target.value,
                          }
                          profileForm.setValue(
                            "go_users",
                            newGoUsers,
                          )
                        }}
                      />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                  <FormItem className="col-span-5">
                    <FormLabel className="ml-3">
                      Nome
                    </FormLabel>
                    <FormControl>
                      <Input placeholder="usuário" />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                  <FormItem className="col-span-3">
                    <FormLabel className="ml-3">
                      Força
                    </FormLabel>
                    <FormControl>
                      <Input placeholder="10k" />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                  {i === totalUsers && (                    
                    <Button
                      className="col-span-1"
                      onClick={() =>
                        setTotalUsers(totalUsers + 1)
                      }
                    >
                      <Plus className="h-4 w-4" />
                    </Button>
                  )}
                </>
              )
            })}
          </fieldset>

          <div className="w-full flex justify-end">
            <Button className="w-max" type="submit">
              Salvar
            </Button>
          </div>
        </form>
      </Form>
    </>
  )
}

这样输入并不是真正可控的,结果似乎有点不可预测。

对于嵌套对象,我能够使用 Zod 和 React Hook Form 以比我预想的更清晰的方式控制事物,例如 this:

export const profileFormValidationSchema = z.object({
  socials_links: z
    .record(z.string(), z.string().url().optional())
    .optional(),
})

...

<FormField
  control={profileForm.control}
  name="socials_links.facebook"
  render={({ field }) => {
    return (
      <FormItem className="col-span-6">
        <FormLabel className="ml-3">
          Facebook
        </FormLabel>
        <FormControl>
          <Input
            type="url"
            placeholder="https://facebook.com/joao.silva"
            {...field}
          />
        </FormControl>
        <FormMessage />
      </FormItem>
    )
  }}
/>

但是对于可创建的字段,我不知道是否有一种简单或干净的方法来做到这一点。无论如何,这些特定的可创建字段没有任何花哨的验证,因此我相信通过

form.setValue(...)
“手动”更改它们应该足够了。

reactjs react-hook-form zod shadcnui
1个回答
0
投票

您可以使用react-hook-forms中的

useFieldArray
来构建这种类型的表单。

API:https://react-hook-form.com/docs/usefieldarray

这里有一个视频可以帮助您。 https://youtu.be/4MrbfGSFY2A

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