React Hook 表单
使用 React Hook Form 和 Zod 构建表单。
表单很棘手。它们是你在 Web 应用中构建的最常见的东西之一,但也是最复杂的之一。
¥Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
精心设计的 HTML 表单是:
¥Well-designed HTML forms are:
-
结构良好且语义正确。
¥Well-structured and semantically correct.
-
易于使用和导航(键盘)。
¥Easy to use and navigate (keyboard).
-
可使用 ARIA 属性和适当的标签访问。
¥Accessible with ARIA attributes and proper labels.
-
支持客户端和服务器端验证。
¥Has support for client and server side validation.
-
风格良好,与应用的其余部分保持一致。
¥Well-styled and consistent with the rest of the application.
在本指南中,我们将介绍如何使用 react-hook-form
和 zod
构建表单。我们将使用 <FormField>
组件通过 Radix UI 组件来编写可访问的表单。
¥In this guide, we will take a look at building forms with react-hook-form
and zod
. We're going to use a <FormField>
component to compose accessible forms using Radix UI components.
功能
¥Features
<Form />
组件是 react-hook-form
库的封装器。它提供了一些东西:
¥The <Form />
component is a wrapper around the react-hook-form
library. It provides a few things:
-
用于构建表单的可组合组件。
¥Composable components for building forms.
-
用于构建受控表单字段的
<FormField />
组件。¥A
<FormField />
component for building controlled form fields. -
使用
zod
进行表单验证。¥Form validation using
zod
. -
处理可访问性和错误消息。
¥Handles accessibility and error messages.
-
使用
React.useId()
生成唯一 ID。¥Uses
React.useId()
for generating unique IDs. -
根据状态将正确的
aria
属性应用于表单字段。¥Applies the correct
aria
attributes to form fields based on states. -
可与所有 Radix UI 组件配合使用。
¥Built to work with all Radix UI components.
-
自带架构库。我们使用
zod
,但你可以使用任何你想要的。¥Bring your own schema library. We use
zod
but you can use anything you want. -
你可以完全控制标记和样式。
¥You have full control over the markup and styling.
剖析
¥Anatomy
<Form>
<FormField
control={...}
name="..."
render={() => (
<FormItem>
<FormLabel />
<FormControl>
{ /* Your form field */}
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
)}
/>
</Form>
示例
¥Example
const form = useForm()
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
安装
¥Installation
用法
¥Usage
创建表单模式
¥Create a form schema
使用 Zod 模式定义表单的形状。你可以阅读有关在 Zod 文档 中使用 Zod 的更多信息。
¥Define the shape of your form using a Zod schema. You can read more about using Zod in the Zod documentation.
"use client"
import { z } from "zod"
const formSchema = z.object({
username: z.string().min(2).max(50),
})
定义表单
¥Define a form
使用 react-hook-form
中的 useForm
钩子创建表单。
¥Use the useForm
hook from react-hook-form
to create a form.
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// 1. Define your form.
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
},
})
// 2. Define a submit handler.
function onSubmit(values: z.infer<typeof formSchema>) {
// Do something with the form values.
// ✅ This will be type-safe and validated.
console.log(values)
}
}
由于 FormField
使用的是受控组件,因此你需要为该字段提供默认值。有关受控组件的更多信息,请参阅 React Hook Form 文档。
¥Since FormField
is using a controlled component, you need to provide a default value for the field. See the React Hook Form docs to learn more about controlled components.
构建你的表单
¥Build your form
我们现在可以使用 <Form />
组件来构建表单。
¥We can now use the <Form />
components to build our form.
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// ...
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
完成
¥Done
就是这样。你现在拥有一个完全可访问的表单,它是类型安全的,具有客户端验证。
¥That's it. You now have a fully accessible form that is type-safe with client-side validation.
示例
¥Examples
有关如何将 <Form />
组件与其他组件一起使用的更多示例,请参阅以下链接:
¥See the following links for more examples on how to use the <Form />
component with other components: