Menu

Хук useActionState в React: формы, ошибки и ожидание

useActionState оборачивает асинхронное действие, хранит значение, которое оно вернуло последним, и сообщает, выполняется ли оно ещё. Сигнатура, действия форм, возврат ошибок валидации и вызов вне формы.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

useActionState это хук React 19, который выполняет асинхронную функцию (действие), сохраняет то, что она возвращает, как состояние и сообщает, выполняется ли она ещё. Вы вызываете его как const [state, formAction, isPending] = useActionState(action, initialState) и передаёте formAction в <form>.

Сначала отправьте пустую форму: действие вернёт объект ошибки, и появится сообщение. Затем введите адрес с @ и отправьте снова. На секунду кнопка покажет «Subscribing...» и станет недоступной.

Синтаксис

const [state, formAction, isPending] = useActionState(action, initialState, permalink?);
  • action это функция, которую нужно выполнить. React вызывает её как action(previousState, ...args). То, что она вернёт (или во что разрешится, если она асинхронная), становится новым state.
  • initialState это состояние до того, как действие хоть раз выполнилось. Используйте ту же форму, что возвращает действие, чтобы компонент мог читать state.message и при первом рендере.
  • state это последний результат.
  • formAction это обёрнутая версия вашего действия. Передайте её в <form action> или <button formAction> или вызывайте сами.
  • isPending равен true с момента начала действия до его завершения.
  • permalink необязателен и важен только для Server Functions во фреймворке: URL, куда должна уйти форма, если её отправят до загрузки JavaScript.

Действие получает предыдущее состояние первым аргументом. Это самая частая ошибка при переводе формы: если написать async function save(formData), в formData окажется старое состояние, и formData.get выбросит ошибку.

Чтение формы через FormData

Когда formAction используется как action формы, React вызывает ваше действие с FormData формы. В нём есть каждое поле с name. Не нужны ни useState для полей, ни обработчик onChange, и не нужно вызывать e.preventDefault(): React сам обрабатывает событие отправки.

Снимите флажок Spicy и отправьте снова: ключ spicy исчезнет из выведенных данных, потому что неотмеченный флажок ничего не отправляет. Действие также читает previousState.count, чтобы прибавить один, и именно поэтому предыдущее состояние идёт первым.

Возврат ошибок валидации

Валидации место в действии. Возвращайте ошибки как часть состояния, а также возвращайте введённые пользователем значения, чтобы форма могла вернуть их на место. После завершения действия формы React сбрасывает её неконтролируемые поля, поэтому поле без defaultValue={state.values...} после неудачной отправки окажется пустым.

Введите имя из одной буквы и короткий пароль, затем отправьте. Появятся обе ошибки, и оба поля сохранят введённое. Удалите defaultValue={state.values.name} и отправьте снова: поле имени очистится после ошибки.

В настоящем приложении последнее слово оставляйте за сервером. Проверки на клиенте дают быструю обратную связь, но любой может отправить запрос в обход вашей формы.

Вызов действия вне формы

formAction это функция, поэтому её может вызвать и кнопка или эффект. Когда вы вызываете её сами, оберните вызов в startTransition. Форма делает это за вас, а простой onClick нет, и без перехода isPending обновляется неправильно (в разработке React выводит об этом ошибку).

Нажмите Load more несколько раз. Каждый вызов начинается с состояния, которое вернул предыдущий, поэтому страницы добавляются по порядку. Если вызвать действие снова, пока оно ещё выполняется, React ставит вызов в очередь и выполняет его после текущего, каждый раз с последним состоянием.

Откуда взялся useActionState

До выхода React 19 этот хук был доступен в canary-сборках как useFormState с импортом из react-dom. Его переименовали в useActionState, перенесли в react и добавили значение isPending. Если вы найдёте такой старый код, исправить нужно импорт и имя:

// React canary, before 19
import { useFormState } from 'react-dom';
const [state, formAction] = useFormState(action, initialState);

// React 19
import { useActionState } from 'react';
const [state, formAction, isPending] = useActionState(action, initialState);

Чтобы показать состояние ожидания в глубоко вложенной кнопке отправки, useFormStatus из react-dom читает статус родительской формы без пропсов. Обзор React 19 перечисляет его вместе с остальными возможностями форм.

Ошибки и когда выбрать другое

Если действие выбрасывает ошибку, React показывает ближайшую границу ошибок вместо обновления состояния. Ошибки, которые пользователь может исправить, например валидацию или занятое имя пользователя, возвращайте как состояние, как в примере с регистрацией. Настоящим сбоям позволяйте выбрасываться.

useActionState нужен для результата действия. Для значения, которое обновляется мгновенно, пока действие выполняется, например счётчика лайков, который должен расти при клике, сочетайте его с useOptimistic. Для ввода, который меняется при каждом нажатии клавиши и читается во время рендера, по-прежнему подходят обычный useState и контролируемое поле.

Частые ошибки

Забытый параметр предыдущего состояния. async function save(formData) получает предыдущее состояние в formData. Для формы сигнатура всегда (previousState, formData).

Поля без name. FormData содержит только поля с атрибутом name. Поля только с id нет в formData.get(...), и он возвращает null.

Контролируемые поля повсюду. Внутри действия формы по-прежнему можно использовать value и onChange, но это не обязательно. Неконтролируемые поля с name и defaultValue проще сочетаются с действиями, а сброс после отправки в React затрагивает только неконтролируемые поля.

Чтение state внутри действия. state из компонента это значение из рендера, который создал обработчик. Внутри действия используйте previousState, который всегда является результатом предыдущего вызова, даже когда несколько вызовов стояли в очереди.

Выброс ошибок для ожидаемых ситуаций. Выброшенная ошибка заменяет форму ближайшей границей ошибок. Неверный пароль это не падение; возвращайте его как состояние.

Часто задаваемые вопросы

Что возвращает useActionState?

Массив из трёх элементов: текущее состояние (значение, которое действие вернуло последним, или начальное состояние, пока оно не выполнялось), новое действие, которое передают форме или вызывают самостоятельно, и isPending, который равен true, пока действие выполняется.

Какие аргументы получает действие?

Сначала предыдущее состояние, затем то, с чем действие было вызвано. В <form action={formAction}> второй аргумент это FormData формы.

Чем useActionState отличается от useFormState?

Это один и тот же хук. Он появился в canary-сборках React как useFormState в react-dom, для React 19 был переименован в useActionState и перенесён в react, и одновременно получил значение isPending.

Можно ли использовать useActionState без формы?

Да. Вызывайте возвращённое действие сами внутри startTransition, например из onClick кнопки. Без перехода isPending обновляется неправильно, а в разработке React выводит ошибку.

Почему моя форма очищается после отправки?

После завершения действия формы React сбрасывает её неконтролируемые поля, как это делает браузер после обычной отправки. Чтобы сохранить значение после ошибки, верните его в состоянии и передайте в defaultValue поля.

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

НАЧАТЬ