<form> - This feature is available in the latest Canary

Canary

React 对 <form> 的扩展目前仅在 React 的 canary 和实验通道中可用。在 React 的稳定版本中,<form> 仅充当 内置浏览器 HTML 组件。了解有关 React 的发布渠道在这里 的更多信息。

¥React’s extensions to <form> are currently only available in React’s canary and experimental channels. In stable releases of React, <form> works only as a built-in browser HTML component. Learn more about React’s release channels here.

内置浏览器 <form> 组件 允许你创建用于提交信息的交互式控件。

¥The built-in browser <form> component lets you create interactive controls for submitting information.

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

参考

¥Reference

<form>

要创建用于提交信息的交互式控件,请渲染 内置浏览器 <form> 组件

¥To create interactive controls for submitting information, render the built-in browser <form> component.

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

请参阅下面的更多示例。

¥See more examples below.

属性

¥Props

<form> 支持所有 普通元素属性。

¥<form> supports all common element props.

action:URL 或函数。当 URL 传递到 action 时,表单的行为将类似于 HTML 表单组件。当函数传递给 action 时,该函数将处理表单提交。传递给 action 的函数可能是异步的,并且将使用包含所提交表单的 表单数据 的单个参数来调用。action 属性可以被 <button><input type="submit"><input type="image"> 组件上的 formAction 属性覆盖。

¥action: a URL or function. When a URL is passed to action the form will behave like the HTML form component. When a function is passed to action the function will handle the form submission. The function passed to action may be async and will be called with a single argument containing the form data of the submitted form. The action prop can be overridden by a formAction attribute on a <button>, <input type="submit">, or <input type="image"> component.

注意事项

¥Caveats

  • 当函数传递给 actionformAction 时,无论 method 属性的值如何,HTTP 方法都将是 POST。

    ¥When a function is passed to action or formAction the HTTP method will be POST regardless of value of the method prop.


用法

¥Usage

在客户端处理表单提交

¥Handle form submission on the client

将函数传递给表单的 action 属性,以便在提交表单时运行该函数。formData 将作为参数传递给函数,以便你可以访问表单提交的数据。这与传统的 HTML 操作 不同,传统的 HTML 操作 只接受 URL。

¥Pass a function to the action prop of form to run the function when the form is submitted. formData will be passed to the function as an argument so you can access the data submitted by the form. This differs from the conventional HTML action, which only accepts URLs.

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>
  );
}

使用服务器操作处理表单提交

¥Handle form submission with a Server Action

渲染带有输入和提交按钮的 <form>。将服务器操作(标有 'use server' 的函数)传递给表单的 action 属性,以在提交表单时运行该函数。

¥Render a <form> with an input and submit button. Pass a Server Action (a function marked with 'use server') to the action prop of form to run the function when the form is submitted.

将服务器操作传递给 <form action> 允许用户在未启用 JavaScript 的情况下或在代码加载之前提交表单。这对于连接速度慢、设备慢或禁用 JavaScript 的用户来说是有益的,并且类似于将 URL 传递到 action 属性时表单的工作方式。

¥Passing a Server Action to <form action> allow users to submit forms without JavaScript enabled or before the code has loaded. This is beneficial to users who have a slow connection, device, or have JavaScript disabled and is similar to the way forms work when a URL is passed to the action prop.

你可以使用隐藏表单字段为 <form> 的操作提供数据。将使用隐藏表单字段数据作为 FormData 的实例来调用服务器操作。

¥You can use hidden form fields to provide data to the <form>’s action. The Server Action will be called with the hidden form field data as an instance of 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>

);
}

你可以调用 bind 方法为其提供额外的参数,而不是使用隐藏表单字段向 <form> 的操作提供数据。除了作为参数传递给函数的 formData 之外,这还会将新参数 (productId) 绑定到函数。

¥In lieu of using hidden form fields to provide data to the <form>’s action, you can call the bind method to supply it with extra arguments. This will bind a new argument (productId) to the function in addition to the formData that is passed as an argument to the function.

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 属性时,形式为 逐步增强

¥When <form> is rendered by a Server Component, and a Server Action is passed to the <form>’s action prop, the form is progressively enhanced.

表单提交期间显示待处理状态

¥Display a pending state during form submission

要在提交表单时显示待处理状态,你可以在 <form> 中渲染的组件中调用 useFormStatus 钩子并读取返回的 pending 属性。

¥To display a pending state when a form is being submitted, you can call the useFormStatus Hook in a component rendered in a <form> and read the pending property returned.

在这里,我们使用 pending 属性来指示表单正在提交。

¥Here, we use the pending property to indicate the form is submitting.

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 钩子的更多信息,请参阅 参考文档

¥To learn more about the useFormStatus Hook see the reference documentation.

乐观地更新表单数据

¥Optimistically updating form data

useOptimistic 钩子提供了一种在后台操作(如网络请求)完成之前乐观地更新用户界面的方法。在表单环境中,此技术有助于使应用感觉更具响应性。当用户提交表单时,界面会立即更新为预期结果,而不是等待服务器的响应来反映更改。

¥The useOptimistic Hook provides a way to optimistically update the user interface before a background operation, like a network request, completes. In the context of forms, this technique helps to make apps feel more responsive. When a user submits a form, instead of waiting for the server’s response to reflect the changes, the interface is immediately updated with the expected outcome.

例如,当用户在表单中键入消息并点击 “发送” 按钮时,useOptimistic 钩子允许消息立即显示在带有 “正在发送…” 标签的列表中,甚至在消息实际发送到服务器之前也是如此。这种 “optimistic” 方法给人以速度和响应能力的印象。然后,该表单尝试在后台真正发送消息。一旦服务器确认已收到消息,“正在发送…” 标签就会被删除。

¥For example, when a user types a message into the form and hits the “Send” button, the useOptimistic Hook allows the message to immediately appear in the list with a “Sending…” label, even before the message is actually sent to a server. This “optimistic” approach gives the impression of speed and responsiveness. The form then attempts to truly send the message in the background. Once the server confirms the message has been received, the “Sending…” label is removed.

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} />;
}

处理表单提交错误

¥Handling form submission errors

在某些情况下,<form>action 属性调用的函数会引发错误。你可以通过将 <form> 封装在错误边界中来处理这些错误。如果 <form>action 属性调用的函数抛出错误,则会显示错误边界的回退。

¥In some cases the function called by a <form>’s action prop throws an error. You can handle these errors by wrapping <form> in an Error Boundary. If the function called by a <form>’s action prop throws an error, the fallback for the error boundary will be displayed.

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 显示表单提交错误

¥Display a form submission error without JavaScript

在加载 JavaScript 包以进行渐进增强之前显示表单提交错误消息需要:

¥Displaying a form submission error message before the JavaScript bundle loads for progressive enhancement requires that:

  1. <form>服务器组件 渲染

    ¥<form> be rendered by a Server Component

  2. 传递给 <form>action 属性的函数是 服务器动作

    ¥the function passed to the <form>’s action prop be a Server Action

  3. useActionState 钩子用于显示错误消息

    ¥the useActionState Hook be used to display the error message

useActionState 有两个参数:服务器动作 和初始状态。useActionState 返回两个值,一个状态变量和一个操作。useActionState 返回的操作应传递给表单的 action 属性。useActionState 返回的状态变量可用于显示错误消息。传递给 useActionState服务器动作 返回的值将用于更新状态变量。

¥useActionState takes two parameters: a Server Action and an initial state. useActionState returns two values, a state variable and an action. The action returned by useActionState should be passed to the action prop of the form. The state variable returned by useActionState can be used to displayed an error message. The value returned by the Server Action passed to useActionState will be used to update the state variable.

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 文档了解有关通过表单操作更新状态的更多信息

¥Learn more about updating state from a form action with the useActionState docs

处理多种提交类型

¥Handling multiple submission types

表单可以设计为根据用户按下的按钮处理多个提交操作。通过设置 formAction 属性,表单内的每个按钮都可以与不同的操作或行为相关联。

¥Forms can be designed to handle multiple submission actions based on the button pressed by the user. Each button inside a form can be associated with a distinct action or behavior by setting the formAction prop.

当用户点击特定按钮时,将提交表单,并执行由该按钮的属性和操作定义的相应操作。例如,表单可能会默认提交一篇文章供审阅,但有一个单独的按钮,其中 formAction 设置为将文章保存为草稿。

¥When a user taps a specific button, the form is submitted, and a corresponding action, defined by that button’s attributes and action, is executed. For instance, a form might submit an article for review by default but have a separate button with formAction set to save the article as a draft.

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>
  );
}


React 中文网 - 粤ICP备13048890号