useActionState es un hook de React 19 que ejecuta una función async (una acción), guarda como estado lo que devuelve y te dice si todavía se está ejecutando. Lo llamas como const [state, formAction, isPending] = useActionState(action, initialState) y pasas formAction a un <form>.
Envía primero el formulario vacío: la acción devuelve un objeto de error y aparece el mensaje. Luego escribe una dirección con @ y vuelve a enviar. Durante un segundo el botón dice "Subscribing..." y está deshabilitado.
La sintaxis
const [state, formAction, isPending] = useActionState(action, initialState, permalink?);
actiones la función a ejecutar. React la llama comoaction(previousState, ...args). Lo que devuelva (o con lo que se resuelva, si es async) se convierte en el nuevostate.initialStatees el estado antes de que la acción se haya ejecutado nunca. Usa la misma forma que devuelve la acción, para que el componente también pueda leerstate.messageen el primer renderizado.statees el último resultado.formActiones una versión envuelta de tu acción. Pásala a<form action>o<button formAction>, o llámala tú mismo.isPendingestruedesde el momento en que empieza la acción hasta que termina.permalinkes opcional y solo importa para las Server Functions en un framework: la URL a la que debe ir el formulario si se envía antes de que cargue JavaScript.
La acción recibe el estado anterior como primer argumento. Es el error más común al migrar un formulario: escribir async function save(formData) te da el estado viejo en formData, y formData.get lanza un error.
Leer el formulario con FormData
Cuando formAction se usa como action de un formulario, React llama a tu acción con el FormData del formulario. Contiene cada campo que tenga un name. No necesitas useState para los inputs ni un manejador onChange, y no llamas a e.preventDefault(): React maneja el evento de envío.
Desmarca Spicy y vuelve a enviar: la clave spicy desaparece de los datos registrados, porque una casilla sin marcar no envía nada. La acción también lee previousState.count para sumar uno, y por eso el estado anterior va primero.
Devolver errores de validación
La validación va en la acción. Devuelve los errores como parte del estado, y devuelve los valores que escribió el usuario para que el formulario pueda volver a ponerlos. Cuando termina una acción de formulario, React reinicia los campos no controlados, así que un campo sin defaultValue={state.values...} volvería vacío después de un envío fallido.
Escribe un nombre de una letra y una contraseña corta, y envía. Aparecen ambos errores y ambos campos conservan lo que escribiste. Quita defaultValue={state.values.name} y vuelve a enviar: el campo del nombre se vacía solo después del error.
En una app real, deja que el servidor tenga la última palabra. Las comprobaciones en el cliente dan una respuesta rápida, pero cualquiera puede enviar una petición sin tu formulario.
Llamar a la acción fuera de un formulario
formAction es una función, así que un botón o un efecto también pueden llamarla. Cuando la llames tú, envuelve la llamada en startTransition. Un formulario lo hace por ti; un onClick normal no, y sin una transición isPending no se actualiza bien (React registra un error sobre ello en desarrollo).
Haz clic en Load more varias veces. Cada llamada parte del estado que devolvió la anterior, así que las páginas se agregan en orden. Si vuelves a llamar a la acción mientras todavía se está ejecutando, React pone la llamada en cola y la ejecuta después de la actual, cada una con el estado más reciente.
De dónde salió useActionState
Antes de que saliera React 19, este hook estaba disponible en las versiones canary como useFormState, importado desde react-dom. Se renombró a useActionState, se movió a react y recibió el valor isPending. Si encuentras código antiguo como este, la solución es el import y el nombre:
// 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);
Para mostrar un estado pendiente en un botón de envío muy anidado, useFormStatus de react-dom lee el estado del formulario padre sin props. La visión general de React 19 lo enumera junto con las demás funciones de formularios.
Errores y cuándo usar otra cosa
Si la acción lanza un error, React muestra el error boundary más cercano en lugar de actualizar el estado. Para errores que el usuario puede corregir, como la validación o un nombre de usuario ocupado, devuélvelos como estado, como en el ejemplo de registro. Deja que los fallos reales lancen.
useActionState es para el resultado de una acción. Para un valor que se actualiza al instante mientras corre la acción, como un contador de me gusta que debe subir al hacer clic, combínalo con useOptimistic. Para una entrada que cambia en cada pulsación y se lee durante el renderizado, useState y un input controlado siguen siendo la herramienta adecuada.
Errores comunes
Olvidar el parámetro del estado anterior. async function save(formData) recibe el estado anterior en formData. La firma siempre es (previousState, formData) para un formulario.
Inputs sin name. FormData solo contiene los campos que tienen un atributo name. Un campo con solo un id falta en formData.get(...), que devuelve null.
Inputs controlados en todas partes. Todavía puedes usar value y onChange dentro de una acción de formulario, pero no hace falta. Los campos no controlados con name y defaultValue encajan mejor con las acciones, y el reinicio de React después del envío solo afecta a los campos no controlados.
Leer el estado dentro de la acción. El state del componente es el valor del renderizado que creó el manejador. Dentro de la acción, usa previousState, que siempre es el resultado de la llamada anterior, incluso cuando se pusieron varias llamadas en cola.
Lanzar errores para casos esperados. Un error lanzado reemplaza el formulario por el error boundary más cercano. Una contraseña incorrecta no es un fallo; devuélvela como estado.
Preguntas frecuentes
¿Qué devuelve useActionState?
Un array de tres elementos: el estado actual (el último valor que devolvió la acción, o el estado inicial antes de que se haya ejecutado), una acción nueva para pasar a un formulario o llamar tú mismo, e isPending, que es true mientras la acción se ejecuta.
¿Qué argumentos recibe la acción?
Primero el estado anterior, y luego lo que se haya pasado al llamar a la acción. En un <form action={formAction}> ese segundo argumento es el FormData del formulario.
¿Cuál es la diferencia entre useActionState y useFormState?
Son el mismo hook. Salió en las versiones canary de React como useFormState en react-dom, se renombró a useActionState y se movió a react para React 19, y al mismo tiempo ganó el valor isPending.
¿Puedo usar useActionState sin un formulario?
Sí. Llama tú mismo a la acción devuelta, dentro de startTransition, por ejemplo desde el onClick de un botón. Sin una transición, isPending no se actualiza bien y React registra un error en desarrollo.
¿Por qué se vacía mi formulario después de enviarlo?
Cuando termina una acción de formulario, React reinicia los campos no controlados del formulario, como hace un navegador después de un envío normal. Para conservar un valor después de un error, devuélvelo en el estado y pásalo al defaultValue del campo.