跳至内容

<Form>

<Form> 组件扩展了 HTML <form> 元素,以提供对 预取 加载 UI 的支持, 提交时进行客户端导航,并实现渐进增强

对于更新 URL 搜索参数的表单很有用,因为它减少了实现上述目标所需的样板代码。

基本用法

/app/ui/search.js
import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      {/* On submission, the input value will be appended to 
          the URL, e.g. /search?query=abc */}
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  )
}

参考

<Form> 组件的行为取决于 action 属性是否传递了 stringfunction

  • action 是一个字符串时,<Form> 的行为类似于使用GET 方法的原生 HTML 表单。表单数据被编码为 URL 中的搜索参数,当表单提交时,它会导航到指定的 URL。此外,Next.js
    • 预取表单可见时的路径,这会预加载共享 UI(例如 layout.jsloading.js),从而加快导航速度。
    • 在表单提交时执行客户端导航,而不是完全页面重新加载。这保留了共享 UI 和客户端状态。
  • action 是一个函数(服务器操作)时,<Form> 的行为类似于 React 表单,在表单提交时执行操作。

action (字符串) 属性

action 是字符串时,<Form> 组件支持以下属性

属性示例类型必填
actionaction="/search"string (URL 或相对路径)
replacereplace={false}布尔值-
scrollscroll={true}布尔值-
prefetchprefetch={true}布尔值-
  • action: 表单提交时要导航到的 URL 或路径。
    • 空字符串 "" 将使用更新的搜索参数导航到同一路由。
  • replace: 替换当前历史状态,而不是将其推送到 浏览器历史记录 堆栈。默认为 false
  • scroll: 控制导航期间的滚动行为。默认为 true,这意味着它将滚动到新路由的顶部,并为前后导航保持滚动位置。
  • prefetch: 控制表单在用户视口中可见时是否应预取路径。默认为 true

action (函数) 属性

action 是函数时,<Form> 组件支持以下属性

属性示例类型必填
actionaction={myAction}function (服务器操作)
  • action:提交表单时要调用的服务器操作。更多信息请参见React 文档

注意:当action为函数时,replacescroll属性会被忽略。

注意事项

  • formAction:可在<button><input type="submit">字段中使用,以覆盖action属性。Next.js 将执行客户端导航,但是这种方法不支持预取。
    • 当使用basePath时,也必须将其包含在formAction路径中。例如:formAction="/base-path/search"
  • key:不支持将key属性传递给字符串action。如果要触发重新渲染或执行变异,请考虑使用函数action
  • onSubmit:可用于处理表单提交逻辑。但是,调用event.preventDefault()将覆盖<Form>的行为,例如导航到指定的 URL。
  • methodencTypetarget:不支持,因为它们会覆盖<Form>的行为。
    • 同样,formMethodformEncTypeformTarget分别可用于覆盖methodencTypetarget属性,使用它们将回退到原生浏览器行为。
    • 如果需要使用这些属性,请改用 HTML <form> 元素。
  • <input type="file">:当action为字符串时,使用此输入类型将匹配浏览器行为,提交文件名而不是文件对象。

示例

指向搜索结果页面的搜索表单

可以通过将路径作为action传递来创建一个导航到搜索结果页面的搜索表单。

/app/page.tsx
import Form from 'next/form'
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  )
}

当用户更新查询输入字段并提交表单时,表单数据将被编码为 URL 中的搜索参数,例如:/search?query=abc

注意:如果将空字符串""传递给action,表单将导航到具有更新搜索参数的同一路由。

在结果页面上,可以使用searchParams page.js属性访问查询,并用它从外部来源获取数据。

/app/search/page.tsx
import { getSearchResults } from '@/lib/search'
 
export default async function SearchPage({
  searchParams,
}: {
  searchParams: { [key: string]: string | string[] | undefined }
}) {
  const results = await getSearchResults(searchParams.query)
 
  return <div>...</div>
}

<Form>出现在用户的视口中时,/search页面上的共享 UI(如layout.jsloading.js)将被预取。提交后,表单将立即导航到新路由,并在获取结果时显示加载 UI。可以使用loading.js设计回退 UI。

/app/search/loading.tsx
export default function Loading() {
  return <div>Loading...</div>
}

为了应对共享 UI 尚未加载的情况,可以使用useFormStatus向用户显示即时反馈。

首先,创建一个组件,在表单处于挂起状态时显示加载状态。

/app/ui/search-button.tsx
'use client'
import { useFormStatus } from 'react-dom'
 
export default function SearchButton() {
  const status = useFormStatus()
  return (
    <button type="submit">{status.pending ? 'Searching...' : 'Search'}</button>
  )
}

然后,更新搜索表单页面以使用SearchButton组件。

/app/page.tsx
import Form from 'next/form'
import { SearchButton } from '@/ui/search-button'
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <SearchButton />
    </Form>
  )
}

使用服务器操作进行变异

可以通过将函数传递给action属性来执行变异。

/app/posts/create/page.tsx
import Form from 'next/form'
import { createPost } from '@/posts/actions'
 
export default function Page() {
  return (
    <Form action={createPost}>
      <input name="title" />
      {/* ... */}
      <button type="submit">Create Post</button>
    </Form>
  )
}

变异后,通常会重定向到新资源。可以使用next/navigation中的redirect函数导航到新的帖子页面。

注意:由于表单提交的“目标”在操作执行之前是未知的,因此<Form>无法自动预取共享 UI。

/app/posts/actions.ts
'use server'
import { redirect } from 'next/navigation'
 
export async function createPost(formData: FormData) {
  // Create a new post
  // ...
 
  // Redirect to the new post
  redirect(`/posts/${data.id}`)
}

然后,在新页面中,可以使用params属性获取数据。

/app/posts/[id]/page.tsx
import { getPost } from '@/posts/data'
 
export default async function PostPage({ params }: { params: { id: string } }) {
  const data = await getPost(params.id)
 
  return (
    <div>
      <h1>{data.title}</h1>
      {/* ... */}
    </div>
  )
}

更多示例请参见服务器操作文档。