Docs
React Hook 表单

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-formzod 构建表单。我们将使用 <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

命令

¥Command

pnpm dlx shadcn@latest add form

用法

¥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.

This is your public display name.

示例

¥Examples

有关如何将 <Form /> 组件与其他组件一起使用的更多示例,请参阅以下链接:

¥See the following links for more examples on how to use the <Form /> component with other components: