useActionState (React 19) - стан, що оновлюється результатом дії (Action). Зручно для форм: дія відправляє дані, а повернене значення стає новим станом - повідомленням про успіх чи помилками.
import { useActionState } from 'react';
async function subscribe(previousState, formData) {
const response = await fetch('/api/subscribe', {
method: 'POST',
headers: { Accept: 'application/json' },
body: formData,
});
if (response.status === 422) {
const { errors } = await response.json();
return { errors, email: formData.get('email') };
}
return { success: true };
}
function SubscribeForm() {
const [state, formAction, isPending] = useActionState(subscribe, { errors: {} });
if (state.success) return <p>Дякуємо за підписку!</p>;
return (
<form action={formAction}>
<input name="email" type="email" defaultValue={state.email} />
{state.errors?.email && <p role="alert">{state.errors.email[0]}</p>}
<button disabled={isPending}>{isPending ? 'Надсилаємо...' : 'Підписатися'}</button>
</form>
);
}
Як працює:
useActionState(reducerAction, initialState)повертає[state, dispatchAction, isPending];reducerAction(previousState, payload)- як редюсер уuseReducer, але може бути асинхронним і мати побічні ефекти. Для формиpayload- цеFormData;isPending- чи виконується дія;- кілька викликів ставляться в чергу й виконуються послідовно: кожен отримує результат попереднього. Подвійне натискання не дасть двох паралельних запитів у довільному порядку;
dispatchActionтреба викликати з дії: передати в<form action>чи<button formAction>, або обгорнути вstartTransition.
Пастки:
- форма скидається після успішної дії - неконтрольовані поля очищуються. Щоб не губити введене при помилці, повертайте значення в стані (
defaultValue={state.email}, як у прикладі); - кинутий виняток у дії скасовує всі дії в черзі й показує найближчий Error Boundary. Очікувані помилки (валідація) - повертати як стан, а не кидати;
- початковий стан і тип результату мають збігатися - інакше TypeScript скаржиться на невідповідність типів.
Server Functions: з Next.js чи іншим RSC-фреймворком дія може бути серверною функцією ('use server') - тоді форма працює навіть до завантаження JavaScript (третій аргумент permalink).