So how can we handle validation errors
like this in a more elegant way?
Well, in Server Actions, as we have it here,
you are not limited to redirecting or throwing errors.
Instead, you can also return values.
You can return response objects to be precise
and I'll show you how
to access those objects in your components in just a second.
So here we could return an object
which maybe has a message field which holds a value
of invalid input.
Though the shape of this object is totally up to you,
it's just important that it's a serializable object,
which means it, for example, shouldn't include any methods
because those would get lost whilst
being sent to the client.
But any simple values like strings, numbers,
nested objects or nested arrays, those values all work.
So that's how we can return a response.
How can we then use that response in that share page
where we trigger that Server Action?
Well, we can use it with help of another hook, another hook
provided by React dom, the useFormState hook.
So not useFormStatus, which we had before, but useFormState.
Sounds similar but is a different hook.
This hook also must be imported
from React dom like this.
And then this hook here works a little bit
like the useState hook that's built into React
because this hook is responsible for managing the state
of this page or of this component, which uses a forum
that will be submitted with help of Server Actions.
And that's precisely the use case
where useFormState can help you
because useFormState needs two arguments.
And the first argument is the actual Server Action
that should be triggered when the form is submitted,
in this case shareMeal.
The second argument you pass
to useFormState is the initial state of this component,
and that simply means the initial value
that should be returned by useFormState
before this action has been triggered
and yielded a response.
So the initial value
that should be used if we haven't received a response
from this action yet, and that could be anything you want,
for example, null
or an object where you have a message field
that holds a value of null.
To reassemble the shape of that response,
we will eventually send back.
Eventually I'll send back an object with this shape
where the message holds a string.
So our initial value here could be the same shape
but null as a value for the message.
Then useFormState will give you an array
with exactly two elements, which might sound familiar
because the default useState hook
provided by React also gives you two elements.
And indeed here the two elements we get here are kind
of similar to the two elements we get from useState
because we get the current state, the current
response you could say, of this page here,
of this component here.
So the latest response returned
by this Server Action in the end
or this initial state if no response has been received yet.
And we get another formAction here,
which we should actually set as a value
for this action prop on the form now.
So instead of setting share meal as a value
for action down here, I'm now setting this formAction,
which I'm getting back from useFormState as a value
for the action down there.
And this must be done so
that useFormState can basically step in
and manage that state for this component.
And that state depends on the execution
of that Server Action and its response.
And that's why useFormState kind of needs to act
as a man in the middle, you could say.
With that, we can then use this state here,
which will essentially be either this year
or any response we got back from ShareMeal
to output data in this component.
Now, in this case, I know that state will
therefore either be an object with a message that's a string
or it'll be an object with a message that's null.
So we can use it down here at the end of the form right
before the submit button, let's say,
to output an error message.
For that, we can check if state.message is truthy,
which means we have a message.
And in that case I wanna output it here
between my paragraph tags.
Now with that, we also need
to tweak the shareMeal action though,
because when passing it as a value to useFormState,
it must have a different shape than it did before.
It should no longer just accept that form data.
Instead, it should now actually accept two parameters
because useFormState will pass two parameters
to shareMeal when it executes it when the
form gets submitted.
The second parameter will still be that submitted data,
but the first parameter will be the previous state.
So either that initial state that we set up here
or any other previous responses
that might have been generated, I don't care about it here,
but still we need to accept it
because form data is now the second argument
we get, not the first.
With that though, we got everything set up that needs
to be set up, but we'll now get an error if we try
to load the page because useFormState, since it in the end
again deals with updating the client, needs
to be executed in a client component and therefore
we should add use client here
in our shareMeal page component file.
We could again try to outsource this into some nested
component, but here I'll stick to this component,
add use client and save that.
And with that, this is looking good.
We got no error and
therefore now we can try submitting an invalid form again.
So here, I'll enter some data like this
and if I now inspect my email field
and I remove the required prop here
and I click share meal, I now get this error message.
Now of course we could make it pop more.
We could make sure that it stands out,
that it's more descriptive and you can do all these things.
You can, for example, tweak that message here,
which in the end is the message
that's showing up on the screen.
But the main takeaway is that this is
how you can show such an error message,
that you can return responses in your Server Actions
and that you can get
and handle them with help of the useFormState hook.