內建瀏覽器 <form> 元件 可讓您建立用於提交資訊的互動式控件。

<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>

參考

<form>

要建立用於提交資訊的互動式控件,請渲染內建瀏覽器 <form> 元件

<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>

請參閱以下更多範例。

屬性

<form> 支援所有 常用元件屬性

action:URL 或函式。當 URL 傳遞給 action 時,表單的行為將與 HTML 表單元件相同。當函式傳遞給 action 時,該函式將處理表單提交。傳遞給 action 的函式可以是非同步的,並且將使用包含已提交表單的 表單數據 的單個參數調用。 action 屬性可以被 <button><input type="submit"><input type="image"> 元件上的 formAction 屬性覆蓋。

注意事項

  • 當函式傳遞給 actionformAction 時,無論 method 屬性的值為何,HTTP 方法都將是 POST。

用法 在客戶端處理表單提交

將一個函式傳遞給表單的 action 屬性,以便在表單提交時執行該函式。formData 將作為參數傳遞給函式,以便您可以存取表單提交的資料。這與傳統的 HTML action 不同,後者只接受 URL。

export default function Search() {
  function search(formData) {
    const query = formData.get("query");
    alert(`You searched for '${query}'`);
  }
  return (
    <form action={search}>
      <input name="query" />
      <button type="submit">Search</button>
    </form>
  );
}

使用伺服器函式處理表單提交

渲染一個包含輸入框和提交按鈕的 <form>。將伺服器函式(標記為 'use server' 的函式)傳遞給表單的 action 屬性,以便在表單提交時執行該函式。

將伺服器函式傳遞給 <form action> 允許用戶在未啟用 JavaScript 或程式碼載入之前提交表單。這對於網路連線速度慢、設備效能低或已停用 JavaScript 的用戶來說非常有利,並且類似於將 URL 傳遞給 action 屬性時表單的工作方式。

您可以使用隱藏的表單欄位將資料提供給 <form> 的 action。伺服器函式將使用隱藏的表單欄位資料作為 FormData 的實例被呼叫。

import { updateCart } from './lib.js';

function AddToCart({productId}) {
async function addToCart(formData) {
'use server'
const productId = formData.get('productId')
await updateCart(productId)
}
return (
<form action={addToCart}>
<input type="hidden" name="productId" value={productId} />
<button type="submit">Add to Cart</button>
</form>

);
}

除了使用隱藏的表單欄位將資料提供給 <form> 的 action 之外,您還可以呼叫 bind 方法來提供額外的參數。這除了將 formData 作為參數傳遞給函式之外,還會將新的參數(productId)綁定到函式。

import { updateCart } from './lib.js';

function AddToCart({productId}) {
async function addToCart(productId, formData) {
"use server";
await updateCart(productId)
}
const addProductToCart = addToCart.bind(null, productId);
return (
<form action={addProductToCart}>
<button type="submit">Add to Cart</button>
</form>
);
}

<form>伺服器元件 渲染,並且將 伺服器函式 傳遞給 <form>action 屬性時,表單會被 漸進式增強

在表單提交期間顯示處理中狀態

要在提交表單時顯示處理中狀態,您可以在 <form> 中渲染的元件中呼叫 useFormStatus Hook,並讀取返回的 pending 屬性。

在這裡,我們使用 pending 屬性來指示表單正在提交中。

import { useFormStatus } from "react-dom";
import { submitForm } from "./actions.js";

function Submit() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}

function Form({ action }) {
  return (
    <form action={action}>
      <Submit />
    </form>
  );
}

export default function App() {
  return <Form action={submitForm} />;
}

要瞭解更多關於 useFormStatus Hook 的資訊,請參閱 參考文件

樂觀更新表單資料

useOptimistic Hook 提供了一種在後台操作(例如網路請求)完成之前樂觀地更新使用者介面的方法。在表單的上下文中,此技術有助於使應用程式感覺更具響應性。當用戶提交表單時,介面會立即使用預期結果更新,而不是等待伺服器的響應來反映變更。

例如,當用戶在表單中輸入訊息並按下「發送」按鈕時,useOptimistic Hook 允許訊息立即出現在清單中,並帶有「發送中...」標籤,即使訊息實際上尚未發送到伺服器。這種「樂觀」的方法給人以速度和響應性的印象。然後,表單會嘗試在後台真正發送訊息。一旦伺服器確認已收到訊息,「發送中...」標籤就會被移除。

import { useOptimistic, useState, useRef } from "react";
import { deliverMessage } from "./actions.js";

function Thread({ messages, sendMessage }) {
  const formRef = useRef();
  async function formAction(formData) {
    addOptimisticMessage(formData.get("message"));
    formRef.current.reset();
    await sendMessage(formData);
  }
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true
      }
    ]
  );

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={formAction} ref={formRef}>
        <input type="text" name="message" placeholder="Hello!" />
        <button type="submit">Send</button>
      </form>
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "Hello there!", sending: false, key: 1 }
  ]);
  async function sendMessage(formData) {
    const sentMessage = await deliverMessage(formData.get("message"));
    setMessages([...messages, { text: sentMessage }]);
  }
  return <Thread messages={messages} sendMessage={sendMessage} />;
}

處理表單提交錯誤

在某些情況下,`<form>` 的 `action` 屬性所呼叫的函式會拋出錯誤。您可以透過將 `<form>` 包裹在錯誤邊界 (Error Boundary) 中來處理這些錯誤。如果 `<form>` 的 `action` 屬性所呼叫的函式拋出錯誤,則會顯示錯誤邊界的後備 (fallback) 內容。

import { ErrorBoundary } from "react-error-boundary";

export default function Search() {
  function search() {
    throw new Error("search error");
  }
  return (
    <ErrorBoundary
      fallback={<p>There was an error while submitting the form</p>}
    >
      <form action={search}>
        <input name="query" />
        <button type="submit">Search</button>
      </form>
    </ErrorBoundary>
  );
}

在沒有 JavaScript 的情況下顯示表單提交錯誤

為了漸進式增強,在 JavaScript 程式碼載入之前顯示表單提交錯誤訊息,需要滿足以下條件:

  1. `<form>` 必須由伺服器元件 (Server Component)渲染
  2. 傳遞給 `<form>` 的 `action` 屬性的函式必須是伺服器函式 (Server Function)
  3. 使用 `useActionState` Hook 來顯示錯誤訊息

`useActionState` 接受兩個參數:一個伺服器函式 (Server Function)和一個初始狀態。`useActionState` 返回兩個值,一個狀態變數和一個動作 (action)。`useActionState` 返回的動作應傳遞給表單的 `action` 屬性。`useActionState` 返回的狀態變數可以用於顯示錯誤訊息。傳遞給 `useActionState` 的伺服器函式返回的值將用於更新狀態變數。

import { useActionState } from "react";
import { signUpNewUser } from "./api";

export default function Page() {
  async function signup(prevState, formData) {
    "use server";
    const email = formData.get("email");
    try {
      await signUpNewUser(email);
      alert(`Added "${email}"`);
    } catch (err) {
      return err.toString();
    }
  }
  const [message, signupAction] = useActionState(signup, null);
  return (
    <>
      <h1>Signup for my newsletter</h1>
      <p>Signup with the same email twice to see an error</p>
      <form action={signupAction} id="signup-form">
        <label htmlFor="email">Email: </label>
        <input name="email" id="email" placeholder="react@example.com" />
        <button>Sign up</button>
        {!!message && <p>{message}</p>}
      </form>
    </>
  );
}

透過useActionState 文件了解更多關於從表單動作更新狀態的資訊

處理多種提交類型

可以設計表單,以便根據使用者按下的按鈕來處理多個提交動作。透過設定 `formAction` 屬性,表單內的每個按鈕都可以與不同的動作或行為關聯。

當使用者按下特定按鈕時,表單會被提交,並執行由該按鈕的屬性和動作定義的相應動作。例如,一個表單預設情況下可能會提交一篇文章以供審閱,但有一個單獨的按鈕,其 `formAction` 設定為將文章儲存為草稿。

export default function Search() {
  function publish(formData) {
    const content = formData.get("content");
    const button = formData.get("button");
    alert(`'${content}' was published with the '${button}' button`);
  }

  function save(formData) {
    const content = formData.get("content");
    alert(`Your draft of '${content}' has been saved!`);
  }

  return (
    <form action={publish}>
      <textarea name="content" rows={4} cols={40} />
      <br />
      <button type="submit" name="button" value="submit">Publish</button>
      <button formAction={save}>Save draft</button>
    </form>
  );
}