<form>
Встроенный в браузер компонент <form>
позволяет создавать интерактивные элементы управления для отправки информации.
<form action={search}>
<input name="query" />
<button type="submit">Поиск</button>
</form>
- Справочник
- Применение
- Обработка отправки формы на клиенте
- Обработка отправки формы с использованием серверной функции
- Отображение состояния ожидания при отправке формы
- Оптимистичное обновление данных формы
- Обработка ошибок при отправке формы
- Обработка ошибок при отправке формы без JavaScript
- Обработка нескольких типов отправок
Справочник
<form>
Чтобы создать интерактивные элементы управления для отправки информации, отрендерите встроенный в браузер компонент <form>
.
<form action={search}>
<input name="query" />
<button type="submit">Поиск</button>
</form>
Пропсы
<form>
поддерживает все общие пропсы HTML-элементов.
action
: URL или функция. Когда в проп action
передаётся URL, форма ведёт себя как компонент HTML-формы. Когда в проп action
передаётся функция, она отвечает за обработку отправки формы. Функция, переданная в action
, может быть асинхронной и вызываться с одним аргументом, содержащим в себе данные отправляемой формы. Проп action
может быть переопределён пропом formAction
для компонента <button>
, <input type="submit">
или <input type="image">
.
Предостережения
- Когда в проп
action
илиformAction
передаётся функция, то HTTP-методом всегда будет POST независимо от значения в пропеmethod
.
Применение
Обработка отправки формы на клиенте
Чтобы обработать форму при отправке, передайте функцию в проп action
. Объект formData
будет передан в функцию в качестве аргумента, чтобы получить доступ к данным, отправленным формой. Это отличается от стандартной работы action
в HTML, который принимает только URL-адреса. После успешного выполнения переданной в action
функции, все неуправляемые поля формы сбрасываются.
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> ); }
Обработка отправки формы с использованием серверной функции
Отрендерите <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">Добавить в корзину</button>
</form>
);
}
Вместо того, чтобы использовать скрытые поля формы для предоставления данных действию <form>
, можно вызвать метод bind
с дополнительными аргументами. Это позволит привязать новый аргумент (productId
) к функции в дополнение к объетку formData
, который передаётся в эту функцию в качестве аргумента.
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>
, а серверная функция передана в проп action
тега <form>
, форма постепенно улучшается.
Отображение состояния ожидания при отправке формы
Чтобы отобразить состояние ожидания во время отправления формы, вы можете вызвать хук useFormStatus
в компоненте, который рендерится внутри тега <form>
, и использовать возвращаемое свойство pending
.
Здесь используется свойство pending
, чтобы указать, что форма отправляется.
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} />; }
Узнать подробнее о хуке useFormStatus
можно в справочной документации.
Оптимистичное обновление данных формы
Хук useOptimistic
позволяет оптимистично обновлять пользовательский интерфейс до завершения фоновой операции, например, сетевого запроса. В контексте форм этот способ помогает делать приложения более отзывчивыми. Когда пользователь отправляет форму, вместо ожидания ответа от сервера для отображения изменений, интерфейс немедленно обновится до ожидаемого результата.
Например, когда пользователь вводит сообщение в форму и нажимает кнопку “Отправить”, хук 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> (Отправка...)</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: "Hello there!", 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} />; }
Обработка ошибок при отправке формы
В некоторых случаях функция, вызываемая пропом action
тега <form>
, выбрасывает ошибку. Эти ошибки можно обрабатывать, обернув <form>
в предохранитель. Если функция, вызываемая пропом action
тега <form>
, выбрасывает ошибку, то будет отображён запасной вариант для предохранителя.
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> ); }
Обработка ошибок при отправке формы без JavaScript
Для отображения сообещния об ошибке при отправке формы до загрузки JavaScript-бандла и соблюдения прогрессивного улучшения необходимо следующее:
<form>
должен рендерится серверным компонентом- функция, передаваемая в проп
action
тега<form>
, должна быть серверной функцией - для отображения сообщения об ошибке должен использоваться хук
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(`Добавлен "${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">Email: </label> <input name="email" id="email" placeholder="react@example.com" /> <button>Подписаться</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}' был опубликован по кнопке '${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> ); }