
<form>
<form>
浏览器内置的 <form> 组件 允许你创建用于提交信息的交互控件。
js
<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>
参考 {/reference/}
<form> {/form/}
若要创建用于提交信息的交互控件,请渲染浏览器内置的 <form> 组件。
js
<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>
属性(Props) {/props/}
<form> 支持所有通用元素属性。
action:一个 URL 或函数。当 action 传入 URL 时,该表单的行为与 HTML 表单组件一致。当 action 传入函数时,该函数将根据 Action 属性模式在 Transition 中处理表单提交。传入 action 的函数可以是异步的,它接收一个包含已提交表单的 表单数据(FormData) 作为参数。action 属性可以被 <button>、<input type="submit"> 或 <input type="image"> 组件上的 formAction 属性覆盖。
注意事项 {/caveats/}
- 当
action或formAction传入函数时,HTTP 方法始终为 POST,无论method属性的值是什么。
用法 {/usage/}
在客户端处理表单提交 {/handle-form-submission-on-the-client/}
将函数传递给表单的 action 属性,以便在提交表单时运行该函数。函数将接收 formData 作为参数,因此你可以访问表单提交的数据。这与传统的 HTML action 不同,后者只接受 URL。action 函数成功后,表单中的所有非受控字段元素将被重置。
js src/App.js
export default function Search() {
function search(formData) {
const query = formData.get("query");
alert(`你搜索了“${query}”`);
}
return (
<form action={search}>
<input name="query" />
<button type="submit">搜索</button>
</form>
);
}
使用服务器函数处理表单提交 {/handle-form-submission-with-a-server-function/}
渲染一个包含输入框和提交按钮的 <form>。将服务器函数(使用 'use server' 标记的函数)传递给表单的 action 属性,以便在提交表单时运行该函数。
将服务器函数传递给 <form action> 可以让用户在未启用 JavaScript 或代码尚未加载的情况下提交表单。这对网络连接慢、设备性能差或禁用 JavaScript 的用户很有帮助,这与将 URL 传递给 action 属性时表单的工作方式类似。
你可以使用隐藏的表单字段为 <form> 的 action 提供数据。服务器函数将收到隐藏字段数据,作为一个 FormData 实例。
jsx
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">加入购物车</button>
</form>
);
}
除了使用隐藏表单字段为 <form> 的 action 提供数据外,你还可以调用 bind 方法为其提供额外的参数。这会除了传递给函数的 formData 参数外,再绑定一个新参数(productId)到该函数上。
jsx
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">加入购物车</button>
</form>
);
}
当 <form> 由服务器组件(Server Component)渲染,并且传递了服务器函数(Server Function)给 <form> 的 action 属性时,该表单会进行渐进增强(Progressive Enhancement)。
在表单提交期间显示待处理状态 {/display-a-pending-state-during-form-submission/}
若要显示表单正在提交的状态,可以在 <form> 中渲染的组件里调用 useFormStatus Hook,并读取返回的 pending 属性。
这里我们使用 pending 属性来指示表单正在提交。
js src/App.js
import { useFormStatus } from "react-dom";
import { submitForm } from "./actions.js";
function Submit() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "提交中..." : "提交"}
</button>
);
}
function Form({ action }) {
return (
<form action={action}>
<Submit />
</form>
);
}
export default function App() {
return <Form action={submitForm} />;
}
js src/actions.js hidden
export async function submitForm(query) {
await new Promise((res) => setTimeout(res, 1000));
}
要了解更多有关 useFormStatus Hook 的信息,请参阅参考文档。
乐观更新表单数据 {/optimistically-updating-form-data/}
useOptimistic Hook 提供了一种在后台操作(如网络请求)完成之前乐观更新用户界面的方法。在表单场景下,这种技术有助于让应用感觉更灵敏。当用户提交表单时,界面会立即以预期结果更新,而无需等待服务器响应。
例如,当用户在表单中输入消息并点击“发送”按钮时,useOptimistic Hook 允许消息在真正发送到服务器之前立即显示在列表中,并附带“发送中...”标签。这种“乐观”方法带来了速度和响应性。然后表单在后台实际发送消息。一旦服务器确认收到消息,“发送中...”标签就会被移除。
js src/App.js
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> (发送中...)</small>}
</div>
))}
<form action={formAction} ref={formRef}>
<input type="text" name="message" placeholder="你好!" />
<button type="submit">发送</button>
</form>
</>
);
}
export default function App() {
const [messages, setMessages] = useState([
{ text: "你好!", sending: false, key: 1 }
]);
async function sendMessage(formData) {
const sentMessage = await deliverMessage(formData.get("message"));
setMessages((messages) => [...messages, { text: sentMessage }]);
}
return <Thread messages={messages} sendMessage={sendMessage} />;
}
js src/actions.js
export async function deliverMessage(message) {
await new Promise((res) => setTimeout(res, 1000));
return message;
}
要了解更多有关 useOptimistic Hook 的信息,请参阅参考文档。
处理表单提交错误 {/handling-form-submission-errors/}
在某些情况下,由 <form> 的 action 属性调用的函数可能会抛出错误。你可以通过将 <form> 包裹在错误边界(Error Boundary)中来处理这些错误。如果 <form> 的 action 属性调用的函数抛出错误,将显示错误边界的 fallback。
js src/App.js
import { ErrorBoundary } from "react-error-boundary";
export default function Search() {
function search() {
throw new Error("搜索错误");
}
return (
<ErrorBoundary
fallback={<p>提交表单时出错</p>}
>
<form action={search}>
<input name="query" />
<button type="submit">搜索</button>
</form>
</ErrorBoundary>
);
}
json package.json hidden
{
"dependencies": {
"react": "19.0.0-rc-3edc000d-20240926",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"react-scripts": "^5.0.0",
"react-error-boundary": "4.0.3"
},
"main": "/index.js",
"devDependencies": {}
}
在没有 JavaScript 的情况下显示表单提交错误 {/display-a-form-submission-error-without-javascript/}
在 JavaScript 包加载之前显示表单提交错误消息,以实现渐进增强,需要满足:
<form>由客户端组件(Client Component)渲染- 传递给
<form>的action属性的函数必须是服务器函数(Server Function) - 使用
useActionStateHook 来显示错误消息
useActionState 接收两个参数:一个服务器函数和一个初始状态。useActionState 返回两个值:一个状态变量和一个动作。useActionState 返回的动作应传递给表单的 action 属性。useActionState 返回的状态变量可用于显示错误消息。传递给 useActionState 的服务器函数返回的值将用于更新状态变量。
js src/App.js
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(`已添加“${email}”`);
} catch (err) {
return err.toString();
}
}
const [message, signupAction] = useActionState(signup, null);
return (
<>
<h1>订阅我的通讯</h1>
<p>使用同一个邮箱注册两次即可看到错误</p>
<form action={signupAction} id="signup-form">
<label htmlFor="email">邮箱: </label>
<input name="email" id="email" placeholder="react@example.com" />
<button>注册</button>
{!!message && <p>{message}</p>}
</form>
</>
);
}
js src/api.js hidden
let emails = [];
export async function signUpNewUser(newEmail) {
if (emails.includes(newEmail)) {
throw new Error("该邮箱地址已添加");
}
emails.push(newEmail);
}
了解有关使用 useActionState 从表单动作更新状态的更多信息。
处理多种提交类型 {/handling-multiple-submission-types/}
表单可以设计为根据用户按下的按钮处理多种提交动作。表单中的每个按钮都可以通过设置 formAction 属性来关联不同的动作或行为。
当用户点击特定按钮时,表单被提交,并执行由该按钮属性和动作定义的对应操作。例如,表单可能默认将文章提交审核,但另一个设置了 formAction 的按钮可以将文章保存为草稿。
js src/App.js
export default function Search() {
function publish(formData) {
const content = formData.get("content");
const button = formData.get("button");
alert(`“${content}”已通过“${button}”按钮发布`);
}
function save(formData) {
const content = formData.get("content");
alert(`你的“${content}”草稿已保存!`);
}
return (
<form action={publish}>
<textarea name="content" rows={4} cols={40} />
<br />
<button type="submit" name="button" value="submit">发布</button>
<button formAction={save}>保存草稿</button>
</form>
);
}
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
