So now that we improved
this form submission experience,
I wanna improve it even further.
And I wanna make sure that we actually also tell the user
if invalid data has been entered because at the moment,
I would be able to submit an empty form,
and that's, of course, not how it should work.
Now, one way of adding validation
is that we simply add some extra attributes
to our form elements.
The required attribute, for example,
can be added to all the inputs and text areas
to make sure that the browser shows an error automatically
without us doing anything if such a field
that's marked as required is empty.
That's useful
and something you definitely should consider doing
because it's a quick and easy way of making your form safer
and giving the user some feedback.
But it's not enough because this can always be disabled.
So here if I add it, a user would be able
to inspect that form element
and simply remove that required prop here,
that required attribute, in the browser developer tools.
Of course, not all users know about these tools,
but a lot of users do.
And therefore with that,
I now would be able to submit the invalid form again.
So that's not enough.
It's a good first step
to improve the user experience, but not enough.
Therefore, you should actually also always
validate submitted data on the server
because that's the only place
where the user can't do anything about your code.
Hence, you should also validate the submitted data here
in that server action.
So here I wanna add a if check
and check if title is maybe undefined
or if title has a length,
maybe after trimming it to remove excess white space,
of zero, which means it would be an empty string.
In that case, I have an error.
The same is true if the content does not exist
or is an empty string.
And if I don't have an image,
so if that's undefined or something like that,
I also wanna return an error.
So when one of these three if checks is met,
I wanna return an error.
Or actually, what I wanna do here
is I wanna create an errors array,
and then I wanna add new error messages
for every check that fails.
So here for the first check, I wanna add a new message
to that array of error messages
that, for example, says, "Title is required."
And then for the second check,
I wanna add a message that says, "Content is required."
And for the third check,
I add a message that says, "Image is required."
That would be one way
of constructing such an error message array.
But of course, having that array here is not enough.
We also need to get it back
to the client side to update the DOM
and show those error messages to the user
in case they did try to submit an empty or invalid form.
And how do we do that?
Well, it would definitely be nice
if we could simply check here in our server action
if this errors array has a length that's greater than zero,
which means it's not empty,
which means we have at least one error.
And if in that case, we could simply return an object here
from our server action that includes that errors array
as a property.
That would be nice to do, right?
If we could just return such an object,
and then we could handle it on the client side.
And it turns out we can do that.
NextJS and React got us covered
because React offers another form-related hook,
the useFormState hook.
So not useFormStatus, which we used before,
but useFormState.
And that's a hook that you can use
to update the form-related UI you're showing
to the user based on some state
that may be changed upon the submission of a form,
which is a complex way of saying you can use this hook
to output any data you return from your formAction,
from your server action in this case.
Therefore, here, I'll import this
useFormState hook from react-dom.
Again, it's imported from react-dom, not from React itself.
And we can now call this hook in our component
that outputs the form, useFormState like this.
So now I'm not calling it in some nested component
as I did it with useFormStatus,
which we called in the formSubmit component
but instead directly in the component
that also outputs the form.
Now, useFormState then takes a value
or actually, two values.
And the first value this hook accepts is the formAction
that should actually eventually be triggered
when the form is submitted.
So therefore, I'll actually call it
after defining that formAction
and then I will pass that formAction,
this createPost function to useFormState
as a first argument.
I'm not executing it.
Instead I'm just passing a pointer to this function
as a first argument to useFormState.
And then as a second argument,
we pass some initial form state
that should be used if the formAction has not executed yet.
And here in my case, that will be an empty object
because the form state I might eventually get
will be this object that has an errors property,
which contains an array of error messages.
That's the form state we may eventually get
because if the form submission succeeds,
if we have valid data, we don't return any new state
that should be shown on the screen.
Instead, we navigate away here.
So the only other state we can achieve is this errors state.
But initially, we, of course, don't have any errors yet
because the user didn't even have a chance
of entering something.
And therefore, my initial state for this form
should be such an empty object
and we'll use that state in just a second, no worries.
But that's what we pass into useFormState.
Then useFormState returns two values,
exactly two values.
Just like the standard useState hook
that's built into React,
that hook would also return an array
with exactly two elements.
useFormState is kind of similar, just related
to forms you could say.
It gives you the current form state.
So that initial state initially
and then thereafter, any other state
that might have been returned by the formAction.
The formAction you passed
as a first argument to useFormState.
And the second element you get back from useFormState
will be an updated formAction,
which is essentially this formAction,
which you pass to useFormState.
But this action here,
which you get back from useFormState,
is essentially such a formAction
to which React is listening
and able to well, get hold of any data returned
by that formAction
because this useFormState hook, then we'll make sure
that any data returned
by your formAction will be set up as a new state.
That's why you get back a new formAction
to which React basically is listening to make sure
that it is able to update that state.
And therefore, it's now this updated formAction,
which you should use as a value
for the action prop of the form
so that React is able to listen to the submission
and update that form state.
Now, with that, if you save that,
you would get an error though
that useFormState only works in a client component.
Therefore, what you should do when using useFormState,
you should add use client at the top of the file.
So here at the top of this page.js file.
With that, if you reload, you get another error though
that we now also have some code that's decorated
with use server inside of a client component,
and that's not allowed, which kind of makes sense I guess
because with use client at the top of this file,
we're telling React
that all the code in here should be executed
on the client side, but then we got some code in there
where we tell React and NextJS
that it should only be executed on the server side,
which are some clashing expectations.
But we can easily work around that problem
by just restructuring our code a bit.