通过 内置浏览器 <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
的函数可以是异步的,并且将使用包含提交表单的 表单数据 的单个参数调用。可以通过 <button>
、<input type="submit">
或 <input type="image">
组件上的 formAction
属性覆盖 action
属性。
注意事项
- 当函数被传递给
action
或formAction
时,无论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>
的操作提供数据。服务器操作将使用隐藏表单字段数据作为 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>
的操作提供数据之外,您还可以调用 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
属性时,该表单将被 渐进增强。
在表单提交期间显示 pending 状态
要在表单提交时显示 pending 状态,您可以在 <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
钩子允许消息立即出现在列表中,并带有“发送中...”标签,即使该消息实际上尚未发送到服务器。这种“乐观”的方法给人以快速和响应迅速的印象。然后,表单会尝试在后台真正发送消息。一旦服务器确认已收到消息,“发送中...”标签就会被删除。
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 包加载之前显示表单提交错误消息以实现渐进增强,需要
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> ); }