表单是 Web 应用中最常见的用户交互方式之一。Next.js 提供了强大的 Server Actions 功能,让我们可以在服务器端处理表单提交,无需创建 API 路由。 在这一部分,我们将学习如何使用 Server Actions 处理表单,如何实现客户端交互,如何处理表单验证,以及如何构建用户友好的表单体验。

Server Actions 是 Next.js 13+ 引入的一个强大功能,它允许我们在服务器端定义函数,然后在客户端组件中直接调用。这大大简化了表单处理流程,无需创建 API 路由。
Server Actions 必须使用 'use server' 指令标记,可以放在单独的文件中,或者直接在服务器组件中定义。
让我们从一个简单的例子开始:
|// app/actions.ts 'use server'; export async function createUser(formData: FormData) { const name = formData.get('name') as string; const email = formData.get('email') as string; // 验证数据 if (!name || !email) { return { error: '姓名和邮箱是必填项' }; } // 创建用户 const user = await saveUser({ name, email }); return { success: true, user }; }
这个 Server Action 接收一个 FormData 对象,从中提取数据,验证后保存用户。如果验证失败,返回错误信息;如果成功,返回成功状态和用户数据。
在表单中使用 Server Actions 非常简单。我们可以直接将 Server Action 传递给表单的 action 属性:
|// app/components/UserForm.tsx import { createUser } from '../actions'; export default function UserForm() { return ( <form action={createUser}> <div> <label htmlFor="name">姓名</label> <input type="text" id="name" name="name"
这个表单会自动将数据提交到 createUser Server Action。Next.js 会处理表单提交,调用 Server Action,并根据结果更新页面。
我们可以使用 useActionState hook(在 React 19+ 中)或 useFormState hook 来处理 Server Action 的结果:
|// app/components/UserForm.tsx 'use client'; import { useActionState } from 'react'; import { createUser } from '../actions'; export default function UserForm() { const [state, formAction, isPending] = useActionState(createUser, null); return ( <form action={formAction}>
useActionState hook 返回三个值:状态、表单 action 和加载状态。我们可以使用这些值来显示错误信息、成功消息和加载状态。
useFormStatus 是 React 18.2/19+ 新增的 hook,可以在表单的任意子组件(比如自定义的提交按钮)中访问表单的实时提交状态。
它会返回一个对象,常用的属性有 pending(表单是否正在提交)。这对于解耦按钮与表单逻辑非常有用,比如让按钮在提交时自动禁用、显示“加载中”等提示,而不需要对表单本身做额外处理。
使用 useFormStatus,我们可以在组件内部灵活地响应表单的提交状态,比如根据 pending 动态切换按钮的禁用状态和展示不同文本,实现更好的用户体验。
|// app/components/SubmitButton.tsx 'use client'; import { useFormStatus } from 'react-dom'; export default function SubmitButton() { const { pending } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? '提交中...' : '提交'} </
这个组件必须在表单内部使用,它会自动获取表单的提交状态。
虽然服务器端验证是必须的,因为只有服务器才最终决定哪些数据被接受和存储。但是在用户填写表单的过程中,客户端验证可以即时发现输入错误,给出清晰的反馈信息,比如字段为空、格式不正确、长度不符等,用户提交前就能迅速修正问题。 这不仅减少了无效请求,还能够提升整体用户体验,让用户不用每次都等待服务器响应错误再做修改,从而加快了交互效率。常见的客户端验证方式有:HTML5 表单内置校验、通过 JavaScript/React 在输入变化时实时检查输入内容等。
使用 HTML5 的内置验证:
|// app/components/UserForm.tsx export default function UserForm() { return ( <form action={createUser}> <div> <label htmlFor="name">姓名</label> <input type="text" id="name" name="name" required minLength=
或者使用 JavaScript 进行更复杂的验证:
|// app/components/UserForm.tsx 'use client'; import { useState } from 'react'; import { createUser } from '../actions'; export default function UserForm() { const [errors, setErrors] = useState<Record<string, string>>({}); const validate = (formData
这个例子展示了如何在客户端进行验证,并在提交前检查数据。如果验证失败,显示错误信息;如果验证通过,调用 Server Action。
对于表单逻辑较复杂、包含多个字段或验证需求较高的场景,手动管理输入状态和错误信息会变得繁琐且容易出错。此时我们可以借助 react-hook-form 库,它能够简化表单注册、数据收集、验证与错误管理等操作。
使用 react-hook-form 后,不仅可以减少样板代码,还能获得更好的性能和可维护性。下面是一个结合 react-hook-form 实现表单验证与提交的完整示例:
|// app/components/UserForm.tsx 'use client'; import { useForm } from 'react-hook-form'; import { createUser } from '../actions'; type FormData = { name: string; email: string; }; export default function UserForm() { const { register, handleSubmit,
react-hook-form 提供了强大的表单管理功能,包括验证、错误处理、提交状态等。
除了处理常规表单数据之外,Server Actions 还能非常方便地实现文件上传功能。我们来看一个更详细的示例来说明整个流程,包括前端表单的设置以及服务端处理文件的过程:
|// app/actions.ts 'use server'; export async function uploadFile(formData: FormData) { const file = formData.get('file') as File; if (!file) { return { error: '请选择文件' }; } // 保存文件 const bytes = await
|// app/components/FileUpload.tsx import { uploadFile } from '../actions'; export default function FileUpload() { return ( <form action={uploadFile} encType="multipart/form-data"> <input type="file" name="file" required /> <button type="submit">上传</button>
注意,文件上传表单需要设置 encType="multipart/form-data"。
为了提升表单体验,我们可以添加一些优化:首先,添加加载状态,让用户知道表单正在提交:
|// app/components/UserForm.tsx 'use client'; import { useActionState } from 'react'; import { createUser } from '../actions'; export default function UserForm() { const [state, formAction, isPending] = useActionState(createUser, null); return ( <form action={formAction}>
其次,在成功提交后重定向或清除表单:
|// app/components/UserForm.tsx 'use client'; import { useActionState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { createUser } from '../actions'; export default function UserForm() { const router = useRouter(); const [state, formAction, isPending] = useActionState
添加防重复提交保护:
|// app/components/UserForm.tsx 'use client'; import { useActionState, useRef } from 'react'; import { createUser } from '../actions'; export default function UserForm() { const formRef = useRef<HTMLFormElement>(null); const [state, formAction, isPending] = useActionState(createUser, null
在这一部分,我们探索了 Next.js 中的表单处理和用户交互。我们学习了如何使用 Server Actions 处理表单提交,如何实现客户端验证,如何使用 React Hook Form,如何处理文件上传,以及如何优化表单体验。 Server Actions 是 Next.js 提供的一个强大功能,它大大简化了表单处理流程。通过合理使用 Server Actions 和客户端验证,我们可以创建既安全又用户友好的表单体验。
在下一个部分,我们将学习元数据和 SEO,了解如何使用 Next.js 的 metadata API 来优化搜索引擎优化,如何设置动态元数据,以及如何实现 Open Graph 和结构化数据。