Hook useActionState
Gerencie o estado do formulário impulsionado por uma função de ação, com estado pendente integrado e aprimoramento progressivo.
Busque em todas as páginas da documentação
Gerencie o estado do formulário impulsionado por uma função de ação, com estado pendente integrado e aprimoramento progressivo.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida - pronto para copiar e colar.
const [state, formAction, isPending] = useActionState(action, initialState);
// assinatura da ação
async function action(previousState: State, formData: FormData): Promise<State> {
// processar dados do formulário, retornar novo estado
}
// Usar em um formulário
<form action={formAction}>
<input name="email" />
<button disabled={isPending}>Enviar</button>
{state.error && <p>{state.error}</p>}
</form>Quando usar isso: Você tem um formulário que envia dados (para uma ação de servidor ou função assíncrona) e deseja que o React gerencie o estado de envio, o indicador pendente e o resultado - com aprimoramento progressivo (funciona sem JavaScript).
"use client";
import { useActionState } from "react";
interface FormState {
message: string;
error: string;
}
async function submitFeedback(
prevState: FormState,
formData: FormData
): Promise<FormState> {
const feedback = formData.get("feedback") as string;
if (!feedback || feedback.trim().length < 10) {
return { message: "", error: "O feedback deve ter pelo menos 10 caracteres." };
}
// Simular atraso do servidor
await new Promise((resolve) => setTimeout(resolve, 1000));
return { message: `Obrigado pelo seu feedback!`, error: "" };
}
const initialState: FormState = { message: "", error: "" };
export function FeedbackForm() {
const [state, formAction, isPending] = useActionState(submitFeedback, initialState);
return (
<form action={formAction} className="space-y-3 max-w-sm">
<label className="block">
<span className="text-sm font-medium">Seu Feedback</span>
<textarea
name="feedback"
rows={3}
className="mt-1 block w-full border rounded px-3 py-2"
required
/>
</label>
<button
type="submit"
disabled={isPending}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
>
{isPending ? "Enviando..." : "Enviar"}
</button>
{state.error && <p className="text-sm text-red-600">{state.error}</p>}
{state.message && <p className="text-sm text-green-600">{state.message}</p>}
</form>
);
}O que isso demonstra:
useActionState gerencia todo o ciclo de vida do formulário: ocioso, pendente, sucesso e erroFormData, retornando o próximo estadoisPending desabilita o botão e exibe texto de carregamento durante o enviouseActionState envolve sua função de ação e retorna uma ação compatível com formulários, o estado atual e um sinalizador pendente.FormData.isPending se torna true sem bloquear a UI.isPending como false.formAction retornado é compatível com o padrão <form action={}>, permitindo o aprimoramento progressivo.| Parâmetro | Tipo | Descrição |
|---|---|---|
action | (prevState: S, formData: FormData) => S ou Promise<S> | Função chamada na submissão do formulário |
initialState | S | Estado inicial antes de qualquer submissão |
permalink | string (opcional) | URL para aprimoramento progressivo (componentes de servidor) |
| Retorno | Tipo | Descrição |
|---|---|---|
state | S | Estado atual (atualizado após cada ação ser concluída) |
formAction | (formData: FormData) => void | Ação para passar para <form action={}> ou <button formAction={}> |
isPending | boolean | true enquanto a ação estiver em execução |
Com ação de servidor (Next.js App Router):
// app/actions.ts
"use server";
export async function createUser(prevState: FormState, formData: FormData) {
const name = formData.get("name") as string;
const user = await db.users.create({ data: { name } });
return { success: true, error: "" };
}
// app/page.tsx
"use client";
import { useActionState } from "react";
import { createUser } from "./actions";
export function CreateUserForm() {
const [state, formAction, isPending] = useActionState(createUser, {
success: false,
error: "",
});
return <form action={formAction}>...</form>;
}Múltiplos botões de envio com formAction:
<form>
<input name="item" />
<button formAction={saveAction}>Salvar Rascunho</button>
<button formAction={publishAction}>Publicar</button>
</form>Ação assíncrona apenas do cliente:
async function loginAction(prev: LoginState, formData: FormData) {
const res = await fetch("/api/login", {
method: "POST",
body: formData,
});
if (!res.ok) return { error: "Credenciais inválidas" };
return { error: "" };
}// Digite o estado explicitamente para clareza
interface ActionState {
success: boolean;
error: string;
data?: UserData;
}
// A ação deve corresponder ao tipo de estado
async function myAction(
prevState: ActionState,
formData: FormData
): Promise<ActionState> {
// ...
return { success: true, error: "" };
}
const [state, formAction, isPending] = useActionState(myAction, {
success: false,
error: "",
});
// state: ActionStateConfusão com useFormState (descontinuado) - React 19 renomeou useFormState para useActionState e adicionou isPending como o terceiro valor de retorno. Correção: Use useActionState de "react", não useFormState de "react-dom".
A ação deve retornar o estado - Se sua ação não retornar um valor, o estado se tornará undefined após a submissão. Correção: Sempre retorne o novo estado da sua função de ação.
O estado é redefinido a cada submissão - O estado anterior é passado como o primeiro argumento; você deve mesclá-lo se quiser preservar os campos. Correção: Espalhe o estado anterior: return { ...prevState, error: "" }.
Usando fora de um formulário - useActionState é projetado para <form action={}>. Chamar formAction manualmente com FormData construído funciona, mas perde o aprimoramento progressivo. Correção: Prefira <form action={formAction}> para melhor compatibilidade.
Serialização de ação de servidor - O estado passado entre servidor e cliente deve ser serializável (sem funções, Dates, Maps). Correção: Use objetos simples com valores primitivos.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
useState + useTransition | Lógica de envio personalizada não vinculada a <form action={}> | Você deseja aprimoramento progressivo |
useReducer | Transições de estado complexas no lado do cliente sem submissão de formulário | Mudanças de estado são impulsionadas por ações de formulário |
| React Hook Form | Validação complexa, erros em nível de campo, formulários dinâmicos | Formulários simples com ações de servidor |
| Ação de servidor sem hook | Mutação de disparar e esquecer, nenhuma atualização de estado do cliente necessária | Você precisa exibir o resultado na UI |
Por que usar useActionState em vez de fetch manual? useActionState fornece estado pendente, tratamento de erros e aprimoramento progressivo em um único hook - sem necessidade de conectar useState + useTransition + try/catch manualmente.
useFormState para useActionState e adicionou isPending como o terceiro valor de retorno.useActionState de "react", não useFormState de "react-dom".formAction retornado pelo hook é compatível com <form action={}>.return { ...prevState, error: "" }.undefined após a submissão, o que pode quebrar sua UI.async function loginAction(prev: LoginState, formData: FormData) {
const res = await fetch("/api/login", {
method: "POST",
body: formData,
});
if (!res.ok) return { error: "Credenciais inválidas" };
return { error: "" };
}
const [state, formAction, isPending] = useActionState(loginAction, { error: "" });isPending se torna true sem bloquear a UI.isPending como false.interface ActionState {
success: boolean;
error: string;
}
async function myAction(
prevState: ActionState,
formData: FormData
): Promise<ActionState> {
return { success: true, error: "" };
}
const [state, formAction, isPending] = useActionState(myAction, {
success: false,
error: "",
});
// state: ActionStateDate, Map e Set falharão durante a serialização.<form>
<input name="item" />
<button formAction={saveDraftAction}>Salvar Rascunho</button>
<button formAction={publishAction}>Publicar</button>
</form>formAction apontando para uma ação diferente.useActionState para submissões de formulário onde você deseja aprimoramento progressivo e estado pendente automático.useState + useTransition para lógica de envio personalizada não vinculada a <form action={}>.useActionState reduz o código repetitivo combinando estado, pendente e ação em um único hook.useActionState usa transições internamenteRevisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥