Registration form with useActionState
This form demonstrates the use of the useActionState hook in React for managing form state and submission. The hook can be used both with server- and client components. It reduces a lot of complexity in handling all the form states, where you can use uncontrolled components more easily.
Read more about it.
The components in the example below don‘t have to manage their own state, as the hook takes care of that. Try it! (The data is not submitted anywhere)
Code example
import { useActionState } from "react";
type FormValues = {
name?: string;
email?: string;
gender?: string;
country?: string;
termsAccepted?: string[];
formSubmitted?: boolean;
};
async function handleSubmit(_: unknown, data: FormData, isValidEmail: boolean) {
const formValues = {
name: (data.get("name") ?? "").toString().trim(),
email: (data.get("email") ?? "").toString().trim(),
gender: (data.get("gender") ?? "").toString(),
country: (data.get("country") ?? "").toString(),
termsAccepted: data.getAll("termsCheckboxes"),
} as FormValues;
const isIncomplete =
["name", "email", "gender", "country"].some(
(key) => !formValues[key as keyof typeof formValues]
) ||
!isValidEmail ||
!formValues.termsAccepted?.includes("termsAccepted1") ||
!formValues.termsAccepted?.includes("termsAccepted2");
if (isIncomplete) {
return formValues;
}
// Simulate form submission
await new Promise<void>((resolve) => {
setTimeout(() => resolve(), 1000);
});
console.log("Form submitted successfully:", formValues);
return {
formSubmitted: true,
};
}
const RegistrationForm = () => {
const [formData, setFormData, isPending] = useActionState(handleSubmit, null);
// or if we need to pass the isValidEmail to the handleSubmit function, then we can do it with an arrow function:
const [formData, setFormData, isPending] = useActionState((prevState, newState) => handleSubmit(prevState, newState, isValidEmail), null);
// We can combine with some state as well, but not for controlling the form inputs
const [isValidEmail, setIsValidEmail] = useState<boolean>(true);
const validateEmail = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value !== '') {
const email = e.target.value;
const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
setIsValidEmail(emailRegex.test(email));
} else {
setIsValidEmail(true);
}
}
return (
<form action={setFormData} className="mb-form flex flex-dir-col gam">
<NameComponent formData={formData} />
<EmailComponent formData={formData} validateEmail={validateEmail} isValidEmail={isValidEmail} />
<GenderComponent formData={formData} />
<CountryComponent formData={formData} />
<TermsComponent formData={formData} />
<button disabled={isPending} type="submit">
{isPending ? "Submitting..." : "Register"}
</button>
{formData?.formSubmitted && (
<p className="success">Registration successful!</p>
)}
</form>
)
}
export default RegistrationForm;