Form Actions in React
Introduction to Form Actions
The Form Actions feature is a new capability built into React that simplifies form submission handling. This feature provides a more streamlined approach to managing form data and validation.
Requirements
To use Form Actions, you need:
- React version 19 or higher
- Check your
package.jsonfile to verify your React version
{
"dependencies": {
"react": "^19.0.0"
}
}If you are using an older version of React, you cannot use this feature and must rely on traditional form handling methods.
Traditional Form Handling
In previous React versions, you would handle form submissions using the onSubmit prop:
function handleSubmit(event) {
event.preventDefault();
// Handle the form submission logic here
}
<form onSubmit={handleSubmit}>
{/* Form fields go here */}
</form>Note: You can still use this approach. There is nothing wrong with it.
Using the Action Prop
What Changed
If you are using React version 19 or higher, you can use the action prop on the form element instead of onSubmit.
<form action={handleSubmit}>
{/* Form fields go here */}
</form>Understanding the Action Prop
The action prop is not really a new prop. Form elements have always supported the action attribute, whether you are using React or not.
- Without React: The action attribute defines the path or URL to which the browser sends the form data when the form is submitted.
- With React: You were preventing this default behavior by calling
event.preventDefault().
Key Difference
When using the action prop with React 19+, the function signature changes:
Before:
function handleSubmit(event) {
event.preventDefault();
// Handle the form submission logic here
}After:
function handleSubmit(formData) {
// formData is a FormData object created by React
// It contains all the form fields and their values
}Important: Make sure you add the name prop to all your form fields (input, select, textarea, etc.).
Validation with Form Actions
There are many different ways to validate form data in React. Let's try a simple approach using an error array to store error messages.
Basic Validation Example
Inside the handleSubmit function:
function handleSubmit(formData) {
let errors = [];
if (!formData.get('username')) {
errors.push('Username is required.');
}
// Add more validation rules here
if (errors.length > 0) {
return { errors: errors };
}
return { errors: null };
}Using useActionState Hook
To access the errors returned from the action function, you can use the useActionState hook from React.
Hook Setup
const [formState, formAction, isPending] = useActionState(signUpActions, { errors: null });Parameters
The useActionState hook takes 2 parameters:
- The action function (e.g.,
signUpActions) - The initial state (e.g.,
{ errors: null })
Return Values
useActionState returns an array with 3 elements:
- formState: In our case, this will be
{ errors: [...] } - formAction: The action function enhanced with more features
- isPending: A boolean indicating whether the form submission is in progress
Updating the Form
You should set the formAction returned by useActionState as the action prop of the form element.
Before:
<form action={signUpActions}>After:
<form action={formAction}>Displaying Error Messages
To display validation errors to the user:
{formState.errors && (
<ul className="errors">
{formState.errors.map((error, index) => (
<li key={index}>{error}</li>
))}
</ul>
)}Troubleshooting Common Errors
Error: formData.get is not a function
When you first try to submit an empty form, you might encounter this error:
Signup.jsx:10 Uncaught TypeError: formData.get is not a function
at signUpActions (Signup.jsx:10:37)Why This Error Occurs
useActionState calls the action function in a different way:
- Without useActionState: The action function receives
formDataas the first parameter. - With useActionState: The action function receives
prevFormStateas the first parameter andformDataas the second parameter.
Solution
Update your function signature:
const signUpActions = (prevFormState, formData) => {
// Now formData is the second parameter
let errors = [];
if (!formData.get('username')) {
errors.push('Username is required.');
}
// Rest of your validation logic
};Improving User Experience
Preserving Valid Input Values
Instead of only returning errors, you can return the entered values along with errors to repopulate the form. This prevents users from losing their valid input when validation fails.
const signUpActions = (prevFormState, formData) => {
let errors = [];
const enteredEmail = formData.get('email');
const firstName = formData.get('firstName');
const lastName = formData.get('lastName');
const role = formData.get('role');
const acquisitionChannel = formData.get('acquisition');
const term = formData.get('terms');
const password = formData.get('password');
const confirmPassword = formData.get('confirmPassword');
// Validation logic here
if (errors.length > 0) {
// Return entered values along with errors to repopulate the form
return {
errors: errors,
enteredValues: {
email: enteredEmail,
firstName: firstName,
lastName: lastName,
role: role,
acquisition: acquisitionChannel,
terms: term ? true : false,
password: password,
confirmPassword: confirmPassword,
},
};
}
return { errors: null };
};Repopulating Form Fields
Change the input fields to use defaultValue to set the initial value:
<input
id="email"
type="email"
name="email"
defaultValue={formState.enteredValues?.email}
/>This approach ensures that valid input is not lost when the form is revalidated.
Summary
Key Takeaways
- Form Actions are available in React 19+ and provide a streamlined way to handle form submissions.
- Use the
actionprop instead ofonSubmitto leverage this new feature. - The action function receives a
FormDataobject directly, not an event object. - The
useActionStatehook helps manage form state and validation errors. - When using
useActionState, the action function signature changes to(prevFormState, formData). - Always add
nameprops to your form fields. - Return entered values along with errors to improve user experience by repopulating the form with valid data.
- Use
defaultValueon input fields to maintain user input during revalidation.
Benefits
- Cleaner code structure
- Built-in form data handling
- Easier state management
- Better user experience with preserved valid input