内置浏览器<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> 包裹在错误边界中来处理这些错误。如果由`<form> 的 `action 属性调用的函数抛出错误,则会显示错误边界的回退内容。

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>服务器组件 渲染
  2. 传递给`<form> 的`action 属性的函数是一个 服务器函数
  3. 使用 `useActionState 钩子来显示错误消息

`useActionState 接受两个参数:一个 服务器函数 和一个初始状态。`useActionState 返回两个值,一个状态变量和一个动作。由`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="[email protected]" />
        <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>
  );
}