What is an Action?
An Action is an async function passed to a form’s action prop, or triggered via startTransition. React automatically tracks its pending state and handles the surrounding form submission, without you needing to call event.preventDefault() or manage a loading flag by hand.
A Plain Action (No Extra State Tracking)
function UpdateNameForm() {
async function updateName(formData) {
const name = formData.get("name");
await saveName(name);
}
return (
<form action={updateName}>
<input type="text" name="name" />
<button type="submit">Save</button>
</form>
);
}What useActionState Adds
useActionState wraps an Action and gives back its current result (like a validation error or success message) and a pending flag — both updated automatically as the Action runs, without separate useState calls for each.
useActionState with Validation
import { useActionState } from "react";
function ChangeNameForm() {
const [error, submitAction, isPending] = useActionState(
async (previousError, formData) => {
const name = formData.get("name");
if (!name) {
return "Name is required";
}
await saveName(name);
return null; // no error
},
null // initial state
);
return (
<form action={submitAction}>
<input type="text" name="name" />
<button disabled={isPending}>{isPending ? "Saving..." : "Save"}</button>
{error && <p className="error">{error}</p>}
</form>
);
}useActionState Arguments and Return Value
| Item | Purpose |
|---|---|
| fn(previousState, formData) | The action function; receives the previous state and the submitted form data |
| initialState | The state value used before the action has run for the first time |
| state | The current state — whatever the action function last returned |
| formAction | Pass this to the form’s action prop (or a button’s formAction prop) |
| isPending | true while the action is in flight |
Comparing to Manual State Management
Before useActionState, the same behavior required separate useState calls for the error/result and the pending flag, plus manually setting and resetting both around the async call — useActionState consolidates all of that into one hook tied directly to the action.
The Manual Equivalent (More Boilerplate)
function ChangeNameForm() {
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
async function handleSubmit(event) {
event.preventDefault();
setIsPending(true);
const name = new FormData(event.target).get("name");
if (!name) {
setError("Name is required");
setIsPending(false);
return;
}
await saveName(name);
setError(null);
setIsPending(false);
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}Best Practice
Reach for useActionState for form submissions that need to track a pending state and a result (success message or validation error) — it removes a very common cluster of boilerplate state management.