在我的表单中,我希望用户能够添加来自其他平台的不确定数量的用户(在本例中,其他平台是 Go(棋盘游戏)服务器):
我的 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(...)
“手动”更改它们应该足够了。
您可以使用react-hook-forms中的
useFieldArray
来构建这种类型的表单。
API:https://react-hook-form.com/docs/usefieldarray
这里有一个视频可以帮助您。 https://youtu.be/4MrbfGSFY2A