Form 组件
<Form> 组件扩展了 HTML <form> 元素,提供了 预取 loading UI、提交时的客户端导航以及渐进式增强。
它对于更新 URL 搜索参数的表单非常有用,因为它减少了实现上述功能所需的样板代码。
基本用法:
import Form from 'next/form'
export default function Page() {
return (
<Form action="/search">
{/* 提交时,输入值将被追加到 URL 中,
例如 /search?query=abc */}
<input name="query" />
<button type="submit">提交</button>
</Form>
)
}参考
<Form> 组件的行为取决于 action prop 传递的是 string 还是 function。
- 当
action是字符串时,<Form>的行为类似于使用GET方法的原生 HTML 表单。表单数据会被编码为 URL 中的搜索参数,当表单提交时,它会导航到指定的 URL。此外,Next.js 会: - 当
action是函数(Server Action)时,<Form>的行为类似于 React form,在表单提交时执行该 action。
action(字符串)Props
当 action 是字符串时,<Form> 组件支持以下 props:
| Prop | 示例 | 类型 | 必需 |
|---|---|---|---|
action | action="/search" | string(URL 或相对路径) | 是 |
replace | replace={false} | boolean | - |
scroll | scroll={true} | boolean | - |
prefetch | prefetch={true} | boolean | - |
action:表单提交时要导航到的 URL 或路径。- 空字符串
""将导航到相同路由并更新搜索参数。
- 空字符串
replace:替换当前历史记录状态,而不是向浏览器历史记录栈推送新状态。默认为false。scroll:控制导航期间的滚动行为。默认为true,这意味着它将滚动到新路由的顶部,并在前进和后退导航时保持滚动位置。prefetch:控制当表单在用户视口中变为可见时是否应预取路径。默认为true。
action(函数)Props
当 action 是函数时,<Form> 组件支持以下 prop:
| Prop | 示例 | 类型 | 必需 |
|---|---|---|---|
action | action={myAction} | function(Server Action) | 是 |
action:表单提交时要调用的 Server Action。更多信息请参见 React 文档。
值得注意的是:当
action是函数时,replace和scrollprops 会被忽略。
注意事项
formAction:可以在<button>或<input type="submit">字段中使用以覆盖actionprop。Next.js 将执行客户端导航,但是这种方法不支持预取。- 当使用
basePath时,你还必须将其包含在formAction路径中。例如formAction="/base-path/search"。
- 当使用
key:不支持向字符串action传递keyprop。如果你想触发重新渲染或执行数据变更,请考虑使用函数action。
onSubmit:可用于处理表单提交逻辑。但是,调用event.preventDefault()将覆盖<Form>的行为,例如导航到指定的 URL。method、encType、target:不支持,因为它们会覆盖<Form>的行为。- 同样,
formMethod、formEncType和formTarget可以分别用于覆盖method、encType和targetprops,使用它们将回退到原生浏览器行为。 - 如果你需要使用这些 props,请使用 HTML
<form>元素。
- 同样,
<input type="file">:当action是字符串时使用此输入类型将匹配浏览器行为,提交文件名而不是文件对象。
示例
导航到搜索结果页面的搜索表单
你可以通过将路径作为 action 传递来创建一个导航到搜索结果页面的搜索表单:
import Form from 'next/form'
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">提交</button>
</Form>
)
}当用户更新查询输入字段并提交表单时,表单数据将被编码为 URL 中的搜索参数,例如 /search?query=abc。
值得注意的是:如果你向
action传递空字符串"",表单将导航到相同路由并更新搜索参数。
在结果页面上,你可以使用 searchParams page.js prop 访问查询,并使用它从外部源获取数据。
import { getSearchResults } from '@/lib/search'
export default async function SearchPage({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const results = await getSearchResults((await searchParams).query)
return <div>...</div>
}当 <Form> 在用户视口中变为可见时,/search 页面上的共享 UI(例如 layout.js 和 loading.js)将被预取。提交时,表单将立即导航到新路由,并在获取结果时显示 loading UI。你可以使用 loading.js 设计回退 UI:
export default function Loading() {
return <div>加载中...</div>
}为了覆盖共享 UI 尚未加载的情况,你可以使用 useFormStatus 向用户显示即时反馈。
首先,创建一个在表单处于待处理状态时显示加载状态的组件:
'use client'
import { useFormStatus } from 'react-dom'
export default function SearchButton() {
const status = useFormStatus()
return (
<button type="submit">{status.pending ? '搜索中...' : '搜索'}</button>
)
}然后,更新搜索表单页面以使用 SearchButton 组件:
import Form from 'next/form'
import { SearchButton } from '@/ui/search-button'
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<SearchButton />
</Form>
)
}使用 Server Actions 进行数据变更
你可以通过向 action prop 传递函数来执行数据变更。
import Form from 'next/form'
import { createPost } from '@/posts/actions'
export default function Page() {
return (
<Form action={createPost}>
<input name="title" />
{/* ... */}
<button type="submit">创建帖子</button>
</Form>
)
}数据变更后,通常会重定向到新资源。你可以使用 next/navigation 中的 redirect 函数导航到新帖子页面。
值得注意的是:由于在执行 action 之前无法知道表单提交的"目标",
<Form>无法自动预取共享 UI。
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// 创建新帖子
// ...
// 重定向到新帖子
redirect(`/posts/${data.id}`)
}然后,在新页面中,你可以使用 params prop 获取数据:
import { getPost } from '@/posts/data'
export default async function PostPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const data = await getPost(id)
return (
<div>
<h1>{data.title}</h1>
{/* ... */}
</div>
)
}更多示例请参见 Server Actions 文档。