useActionState to Simplify Form Actions

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

Professional NextJS Lessons

1.Course Intro
free
⏱️ 2:05
2.Get Setup
free
⏱️ 2:05
3.Project Template
free
⏱️ 5:45
4.Introducing FullStack React
free
⏱️ 4:57
5.App Router Reference
free
⏱️ 7:08
6.NextJS Server vs. "use client" Rendering
⏱️ 5:01
7.Nesting Server and Client Components
⏱️ 8:51
8.Client Only Components
⏱️ 3:48
9.Global CSS in NextJS
⏱️ 6:30
10.CSS Modules and SASS
⏱️ 4:15
11.Tailwind CSS
⏱️ 5:17
12.NextJS Custom Fonts
⏱️ 4:08
13.Static vs Dynamic Rendering
⏱️ 4:55
14.NextJS Link Component
⏱️ 3:20
15.NextJS Link Prefetching
⏱️ 5:49
16.Navigation Scroll with Link
⏱️ 2:02
17.Replace Navigation History
⏱️ 1:35
18.NextJS Image Component
⏱️ 7:01
19.Dynamic Image URLs
⏱️ 5:34
20.Safe Colocation with Private Folders
⏱️ 2:12
21.NextJS Route Groups
⏱️ 3:07
22.Dynamic Routes
⏱️ 5:37
23.NextJS API Routes
⏱️ 6:03
24.NextRequest and NextResponse
⏱️ 2:41
25.Cookies in API Handlers
⏱️ 4:25
26.React Suspense Simplified
⏱️ 2:23
27.Power of Suspense Boundaries
⏱️ 3:52
28.use Promises
⏱️ 3:24
29.Stream Promise Results from Server to Client
⏱️ 3:03
30.use Requires a Cached Promise
free
⏱️ 2:30
31.Suspense Error Boundaries
⏱️ 4:42
32.React Server Functions
⏱️ 4:50
33.Server Actions for Powerful Forms
⏱️ 5:35
34.useActionState to Simplify Form Actions
⏱️ 6:38
35.React Serializable Types
⏱️ 2:42
36.Create Dynamic Images with NextJS ImageResponse
⏱️ 5:54
37.Deploy NextJS Application to Production
⏱️ 3:51
38.NextJS Environment Variables Masterclass
⏱️ 3:50
39.Deploying and Managing Environment Variables
⏱️ 4:38
40.Using Databases for Dynamic Server Side State
free
⏱️ 7:34
41.Mastering NextJS Layout Files
⏱️ 4:54
42.Client State Management with NextJS
⏱️ 3:48
43.Client State Management In Action
⏱️ 8:04
44.Using Client State Management Libraries
⏱️ 5:25
45.Detecting Server vs Client Runtime and useEffect
⏱️ 4:33
46.Demystifying ISR vs SSR vs SSG
⏱️ 3:07
47.Static Site Generation or SSG with NextJS
⏱️ 3:16
48.Incremental Static Regeneration (ISR) Simplified
⏱️ 2:51
49.API Routes Caching with NextJS
⏱️ 3:05
50.Reference Guide to Caching for Modern NextJS
⏱️ 2:47
🚨 Work In Progress 🚧
Subscribe for Launch

useActionState to Simplify Form Actions

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Core Concept

Instead of passing the server function directly to the form action prop, the useActionState can be used to wrap the server function allowing us to hook into its execution lifecycle.

It's worth noting the signature:

const [state, action, isPending] = useActionState(serverFunction, initialState);

With explanation:

  • state starts off as initialState and is updated based on last returned value from serverFunction.
  • action is the wrapped version of serverFunction you should use for the form.action prop.
  • isPending is true if a call to action is pending (e.g. because of form submission).

Additionally the serverFunction should have the signature:

async function serverFunction(prevState: State, formData: FormData): Promise<State>;

With explanation:

  • prevState is passed by the client. It will be initialState to begin with and then be whatever was last returned by the serverFunction.
  • formData is the FormData serialized by the browser for the form being submitted.
  • The return should be the updated State we want to store into the action state.

Synchronize Client and Server State

A practical application of the useActionState hook is to synchronize the form state between the server ↔️ client.

This is demonstrated in the below example.

lib/functions.ts
app/page.tsx
lib/Likes.tsx
"use server";

export type ServerState = {
likes: number;
};

export async function initial(): Promise<ServerState> {
return { likes: 0 };
}

export async function update(prevState: ServerState): Promise<ServerState> {
await new Promise((resolve) => setTimeout(resolve, 1000));
return { likes: prevState.likes + 1 };
}

Displaying Form Submission Results

Another common use case for useActionState is to display the responses (e.g. success/error/validation messages) returned by the server on form submission.

This is demonstrated in the below example.

lib/functions.ts
app/page.tsx
lib/Product.tsx
"use server";

export type AddToCartResponse = {
type: "error" | "success";
message: string;
};

export async function addToCart(
_state: unknown,
formData: FormData
): Promise<AddToCartResponse> {
await new Promise((resolve) => setTimeout(resolve, 1000));
const product = formData.get("product");
if (product === "wheels") {
return {
type: "error",
message: `Out of stock: ${product}`,
};
}
return {
type: "success",
message: `Added to cart: ${product}`,
};
}
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

Instead of passing the server function directly to the form action prop, the use action state can be used to wrap the server function, allowing us to hook into the function lifecycle. A practical application of the use action state hook is to synchronize the form state between the server and the client. We start off with the use server directive and here we are going to define the structure of the state that we plan to synchronize between the server and the client. It's going to be a simple state that contains likes of type number. We export a function that is going to expose the initial value of the state. Here. We could do some database access,

00:33

but to keep things simple, which is going to keep it in memory. Next, we can export as many mutating functions as we want. We are going to keep things simple and export a single update action in order to demonstrate the use action state hook. All that are state to date functions need to do is to take a previous value of the state and return an updated state back to the client To demonstrate how the client can be notified about a pending state transition. We will add a time delay and then eventually return the new state, which will simply increment the likes by one. Within the root page of our application, we fetch the initial state using a call

01:07

to initial evaluate the results and then pass it down to a client side likes component. The client component will accept the initial state from the parent server component and the objective is going to be to keep it synchronized with the server as the updates happen. We start off the client component with use client, bring in the type definition for the server state along with the update action, and then we bring in the use action state from Core React within the client component. We accept the initial state provided as a prop by the parent component, and then we pass the update action along

01:39

with the initial state to the use action state hook. The Use Action State hook returns a couple of three values. The first is the current state, which will be the initial state originally and will continue to be the updated value based on whatever is returned every single time we call the server function. And in order to ensure that it works this way, instead of calling the update method directly, we need to invoke the method return by use action state, which here we are storing in the variable called action. Finally, the third member is a bullion, which is going to be true whenever a call to the action is pending. And that's most of what you need

02:13

to know about use Action State. So let's use these values within our user interface. First off, we display the current value of the state or likes. Then we have a form with the action wide to the action return by use action state. And finally we have a button within the form that allows the user to submit the form and we will disable this if there is a pending action. Additionally, within the button, if there is a pending action, we will show the R glasss. Otherwise we will show the thumbs up with our user interface completed. Let's take a look at it. Within the browser we see the initial count of likes along

02:45

with our form containing the thumbs up. If you submit the form, it goes into a pending state and eventually comes back with the updated likes and everything seems to work as expected. Another common use case for use action state is to display Success and error messages returned by the server on form submission. For this example, we will create a new server function and our objective from this example is going to be to return a well-structured response from our server function, which over here we are typing as add to cart response. Next, we create a server function that we plan to use with use Action State.

03:18

We already know that the use action state hook will invoke a server function with the first argument pointing to a state that it maintains over the different mutations. We don't need that for our example, so we simply prefix the variable with underscore to indicate that it's not used and also type it as unknown. So TypeScript rule screen at us if we end up using it by mistake, use action State also passes a second perimeter to a server function, which is going to be the form data computed by the browser as a part of submitting the form. This form data is the same argument that we would get if we passed server function

03:50

directly into the action prop of a form. Finally, this function should return the well typed add to cart response. We start off the function with a simple delay to ensure that we can see the pending state of the function on the client. Next, we can slice and dice the form using the form data. Over here, we expect to be passed in a product name and then we can use these values from the input form however we want. Here we are adding some mock business logic. If the product is wheels, we are going to pretend that it is outta stock, otherwise you pretend that we've added it to the user's database cart. On our homepage, we will display two products.

04:22

One will be a skateboard and one will be the wheels. And the product component is going to contain the form that we are going to wire to that add to cart server function. With use Action State, we start off the product module bringing in the add to cart server function along with the use action state hook. As we saw previously, our product component was passed in a product name as a prop, which we happily accept. And then utilizing the Use Action state hook, we create a wrapper around the add to cart server function. The state maintained by use Action state will only be used to track the response of add to cart, which initially we do not have and

04:55

therefore we provide the value of. Now. Eventually, once the action is invoked, we will get back the server response within our user interface. We start off by displaying the product image and then if we do not have a server response yet, we create a form with the action wide to the action prop provided by use Action state. And as we saw, our add to cart function expects to be passed in a form data containing a product name. So for that purpose we create an input that is hidden and read only has the name product with the value pointing to the product that was passed in. For this component, having a hidden input field ensures

05:29

that it's not shown to the user, but the value is submitted as a part of the form data, which we then use within add to cart. Finally, we have the submit button, which allows the user to submit the form, which will be disabled with a spending if a form submission is already taking place. This is the form that we are showing while we do not have a server response, but when we do have the server response, instead of showing the form, we will display a nice P tag rendering out the server response message. That's it for the User interface of this application. Designed to submit a form to a server function and then display the server response back to the user.

06:03

Let's take a look at it within the browser, our homepage contained two product components, each with their own add to cart forms. The first one is skateboard, which we can successfully add to the cart. The second one is wheels, and our server is designed to return a mock response that it is out of stock. And that is exactly what we see when we try to submit the form with add to cart. To recap this pattern of setting the use action state, initial state to null along with the server function, current state to unknown is a common pattern when you want to utilize use action state to simply read the server response.

Professional NextJS

Professional NextJS

1.Course Intro
free
⏱️ 2:05
2.Get Setup
free
⏱️ 2:05
3.Project Template
free
⏱️ 5:45
4.Introducing FullStack React
free
⏱️ 4:57
5.App Router Reference
free
⏱️ 7:08
6.NextJS Server vs. "use client" Rendering
⏱️ 5:01
7.Nesting Server and Client Components
⏱️ 8:51
8.Client Only Components
⏱️ 3:48
9.Global CSS in NextJS
⏱️ 6:30
10.CSS Modules and SASS
⏱️ 4:15
11.Tailwind CSS
⏱️ 5:17
12.NextJS Custom Fonts
⏱️ 4:08
13.Static vs Dynamic Rendering
⏱️ 4:55
14.NextJS Link Component
⏱️ 3:20
15.NextJS Link Prefetching
⏱️ 5:49
16.Navigation Scroll with Link
⏱️ 2:02
17.Replace Navigation History
⏱️ 1:35
18.NextJS Image Component
⏱️ 7:01
19.Dynamic Image URLs
⏱️ 5:34
20.Safe Colocation with Private Folders
⏱️ 2:12
21.NextJS Route Groups
⏱️ 3:07
22.Dynamic Routes
⏱️ 5:37
23.NextJS API Routes
⏱️ 6:03
24.NextRequest and NextResponse
⏱️ 2:41
25.Cookies in API Handlers
⏱️ 4:25
26.React Suspense Simplified
⏱️ 2:23
27.Power of Suspense Boundaries
⏱️ 3:52
28.use Promises
⏱️ 3:24
29.Stream Promise Results from Server to Client
⏱️ 3:03
30.use Requires a Cached Promise
free
⏱️ 2:30
31.Suspense Error Boundaries
⏱️ 4:42
32.React Server Functions
⏱️ 4:50
33.Server Actions for Powerful Forms
⏱️ 5:35
34.useActionState to Simplify Form Actions
⏱️ 6:38
35.React Serializable Types
⏱️ 2:42
36.Create Dynamic Images with NextJS ImageResponse
⏱️ 5:54
37.Deploy NextJS Application to Production
⏱️ 3:51
38.NextJS Environment Variables Masterclass
⏱️ 3:50
39.Deploying and Managing Environment Variables
⏱️ 4:38
40.Using Databases for Dynamic Server Side State
free
⏱️ 7:34
41.Mastering NextJS Layout Files
⏱️ 4:54
42.Client State Management with NextJS
⏱️ 3:48
43.Client State Management In Action
⏱️ 8:04
44.Using Client State Management Libraries
⏱️ 5:25
45.Detecting Server vs Client Runtime and useEffect
⏱️ 4:33
46.Demystifying ISR vs SSR vs SSG
⏱️ 3:07
47.Static Site Generation or SSG with NextJS
⏱️ 3:16
48.Incremental Static Regeneration (ISR) Simplified
⏱️ 2:51
49.API Routes Caching with NextJS
⏱️ 3:05
50.Reference Guide to Caching for Modern NextJS
⏱️ 2:47
🚨 Work In Progress 🚧
Subscribe for Launch