- Published on
useActionState – React Hook for Simplified State Management
- Authors
- Name
- Roy Bakker
When working with React 19, the introduction of the useActionState
hook brings significant improvements to managing asynchronous operations within forms. Recently available in React's Canary and experimental channels, this hook is designed to update the state based on the result of form actions. This makes it easier to handle async functions directly within form components, enhancing the user experience.
To fully leverage useActionState
, it's essential to use a framework that supports React Server Components. The hook simplifies state management by allowing state updates to be triggered by various async operations, creating a more seamless and responsive interface. This feature is a valuable addition for any React project aiming to implement progressive enhancement techniques effectively.
Reference
useActionState(actionFunction, startState, pageUrl?)
I use the useActionState
hook to update component states whenever a form action is triggered. This hook takes an existing form action function and an initial state, returning a new action alongside the latest form state. This updated state is then accessible to the function provided.
Here’s a basic example:
import { useActionState } from 'react'
async function increment(previousState) {
return previousState + 1
}
function StatefulForm() {
const [state, formAction] = useActionState(increment, 0)
return (
<form>
{state}
<button formAction={formAction}>Increment</button>
</form>
)
}
In this setup, useActionState
initializes with the state value and the action passed to the hook. The increment
function here updates the state by adding one each time a form action is triggered.
The form state is derived from the value returned by the action during the last submission. If the form hasn't been submitted yet, the state remains as the initial value given.
When working with Server Actions, useActionState
ensures that the server’s response to a form submission is visible even before hydration is done. This is particularly useful for dynamic pages where the response needs to be shown quickly.
Parameters
- Function (
fn
): This is the main function called when the form is submitted. It initially receives the previous state of the form, or theinitialState
if it's the first call. This is followed by the form's action arguments. - Initial State (
initialState
): This value sets the initial state of the form. It can be any serializable value. Once the function is invoked, the initial state gets overwritten by the returned value of the function. - Optional Permalink (
permalink
): This is a string containing a unique page URL that the form aims to modify. It comes in handy for pages with dynamic content, working alongside progressive enhancement. If the form submits before JavaScript loads, the browser navigates to this URL. Make sure the same form component is rendered on this new page to maintain the state.
Returns
useActionState
yields an array that includes:
- Current State: On the first render, it's the
initialState
. After invoking the action, it updates to the value returned by the action. - New Action: This new action can be used as the
action
prop for the form component or theformAction
prop for any button within the form.
Caveats
- With React Server Components:
useActionState
allows interactive forms even before client-side JavaScript executes. Without Server Components, it behaves like local state management within the component. - Function Parameters: The function used with
useActionState
expects an extra initial or previous state argument, which makes its usage distinct compared to directly using it as a form action without the hook.
By using useActionState
, I can manage form actions and state changes efficiently, ensuring that state updates are handled seamlessly within my React components.
Usage
Using Data from a Form Action
At the top of your component, call useActionState
to get the most recent return value of an action after a form is submitted.
import { useActionState } from 'react'
import { action } from './actions.js'
function MyComponent() {
const [state, formAction] = useActionState(action, null)
return <form action={formAction}>{/* ... */}</form>
}
The useActionState
hook gives you an array with two elements:
- State: Starts with the initial state you gave and updates to the return value of the action after submission.
- Form Action: A new action that you can use as the
action
prop in the form.
When the form is submitted, the function you provided is called. Its return value becomes the new state of the form.
The action function gets the current state of the form as its first argument. During the first submission, this is the initial state. For later submissions, it’s the return value from the previous call. Remaining arguments stay the same as if useActionState
was not in use.
function action(currentState, formData) {
// ...
return 'next state'
}
This approach helps manage state and actions in forms effectively, making React components more predictable and easier to handle during form submissions. This method also allows automatic tracking of loading states, ensuring your form component is always up-to-date with the backend API calls.
Troubleshooting
My action cannot read the submitted form data anymore
When I use useActionState
, an extra parameter is added before the form data. This means that the form data becomes the second argument instead of the first. The first argument now represents the current state of the form.
function action(currentState, formData) {
// Handle form data with new argument order
}
This change affects data handling in server actions, form submissions, and error management. Adjust the parameters to ensure the correct data is captured. Be mindful of these argument shifts to avoid issues with request processing and target actions.