跳至內容

<Form>

<Form> 元件擴展了 HTML <form> 元素,以提供 預取 載入 UI 的功能, 提交時的客戶端導航漸進式增強

它適用於更新網址搜尋參數的表單,因為它減少了實現上述功能所需的樣板程式碼。

基本用法

/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 屬性傳入的是 string 還是 function

  • 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}boolean(布林值)-
scrollscroll={true}boolean(布林值)-
prefetchprefetch={true}boolean(布林值)-
  • 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 將執行 client-side 導航,但這種方法不支援預取。
    • 使用 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 屬性,表單將會導向到具有更新後搜尋參數的相同路由。

在結果頁面上,您可以使用 page.jssearchParams 屬性存取查詢,並使用它從外部來源擷取資料。

/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 設計 fallback 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>
  )
}

如需更多範例,請參閱 伺服器動作 文件。