So it's great that we can now load data
with that loader feature
but what about submitting data?
What about creating a new post?
At the moment, this doesn't work.
And of course, it should work.
And for that, we can also leverage React Router.
It's the NewPost component
where we have this form in the end.
And we've got a bunch of code
for getting the user input
and for then preventing the default behavior of the browser
and doing something with the data.
Now, what we could do
is we could grab that code for sending that data
to the backend,
and cut it from addPostHandler here
and actually get rid of addPostHandler
in PostsList, making this component even leaner.
And instead add it here
in this NewPost component
instead of calling onAddPost and onCancel.
Instead of doing that, we could send that postData
to the backend right from inside the NewPost component.
And indeed, this would work
but it's still a bunch of code we would have to write here,
especially since we probably
also wanna close the modal thereafter,
which means that we then also have to add extra code
for navigating to a different route programmatically.
A better and more elegant approach
is to also use a special feature offered
by React Router
because just as you can add loaders to your routes
to load data before it gets activated
and before the component gets rendered,
you can add actions to routes.
So to this route here, the create-post route,
we can add an action by setting the action property.
And just like loader, action wants a function
as a value.
This function, however, will now be triggered
when a form is submitted in that route,
which means that here in the NewPost component,
we could export such a action function
to again have that code close to the component
to which it belongs.
And then in here, we could add our code
for sending that request.
Like this.
Now, we still need to get the postData
but that's how we could get started.
But how do we now get the data
that's entered into the form by the user?
Well, previously, we had to manage our own state for that.
Now when we embrace React Router,
this all gets much leaner again.
We can delete that state
and delete these functions that updated the state.
We can also delete this submitHandler
and therefore, also delete the useState import.
And get rid of the onSubmit event listener
on the form element
and get rid of the onChange listeners on the inputs.
So we already got rid of a lot of code here.
And instead, all we have to do here
is add a name attribute to our inputs.
So in this case, to the text area here.
And then assign a name,
which we later wanna use for extracting
and submitting our data.
So for example, here my posts have a body,
and an author field here.
And if I wanna stick to that to body and author,
I could give my text area here the name body,
and this here, the name author.
So that I already used the keys
which I wanna use for storing my data here
on these inputs.
But what's the idea behind this name attribute?
Well, it's a default HTML attribute
that can be added to form inputs
and by default, if a form is submitted,
the browser will, as mentioned earlier,
generate a request
and it will try to send it to the server
that served that website,
which would be wrong
because that server that's serving that website
doesn't have any code to handle this request
with the form data on the backend
because we have no backend code as part of this React app.
But that's now where React Router comes in.
We can import a special component from React Router,
and that would be the Form component with a capital F.
And if we use that instead
of the regular built-in Form element,
React Router will handle the form submission
and it will prevent that browser default
of sending a request
but it will still gather all the input data,
still generate an object with that data for us,
and that's the important part,
call the action that is assigned to the route,
which contains the form,
the form with the capital F.
So therefore now with this Form component being used,
React Router will in the end make sure
that this action here gets executed
and therefore, we can now also import this action function,
which I'm exporting here in NewPost.
Import this from the NewPost component file here.
The action.
And maybe also give it an alias
in case we had multiple actions
from different routes to avoid clashes.
That could be the newPostAction therefore.
And assign that as a value
for the action property down there, like this.
So that in the end,
it's now this action function
that will be executed by React Router
if this form here is submitted.
Now one last thing I'll do here
is I'll add the method prop here
to this Form component and set it to post.
Now, it's important to understand
that no actual post request will be sent anywhere
because this is still all client-side code
but what React Router will do behind the scenes
is it will generate a request object
with that form data included in it.
And it will give this request object a method,
which we could then use here on the action
to find out which form was submitted
when this cation was triggered
in case we had multiple forms
that belong to the same route with the same action.
And even without that, it's kind of a good idea
to use the semantically correct HTTP verb
and since we're creating a new post here,
the post method makes sense
because that's typically the HTTP method
that is used for creating resources.
So with that, no request is sent to the backend.
Instead, this action will be executed
and here, you actually get some data argument.
This is passed in automatically by React Router.
And that's not the data of the form,
instead that is an object,
which for example, has a request property
with that request object that's generated
and built by React Router.
So React Router calls this action function
and passes this object
that includes the request object
that was generated to this action function.
Therefore here, we can actually use object destructuring
to get hold of that request object like this.
And now this request object has a formData method.
And when you execute that formData method,
you get access to the data encoded in that form,
so the data that was extracted by React Router
when it analyzed that form and called that action.
Now, formData actually yields a promise,
so we should turn this into a async function
so that we can use await.
And then here we got our formData object.
And that is a rather complex object.
It's not a plain key-value store.
Instead, it's a object that, for example,
has a get method,
which you could call to get the value provided
for the body field, like this.
That's how you could then extract the data
that was provided by the user.
Now, an easier approach here
is to instead create a postData constant
and use the built-in Object class
and call fromEntries and pass this formData object to it.
Under the hood, this will simply then create
a basic key-value object
where you have a body key
with some value, and an author key with some value
in our case here since we have body and author
as named values in this form.
So that's how we can extract the formData
with help of React Router.
Again, all still happening on the client side here.
And therefore now we got the postData we need
for sending that request.
Here we can also await this to wait
for the request to be sent.
We could also get the response
and analyze the response,
for example, to find out if something went wrong
but I'll not do that here.
And instead now, after sending this request,
we can also call another function provided
by react-router-dom
and that would be the redirect function.
This is imported from react-router-dom
and you can call it in your action and loader functions
to return the result of calling this function here.
Now, what redirect does is it generates a response object,
which in the end is then returned by this action.
And if you return such a response object,
React Router will look into that object
and if it's a redirect response,
which is the kind of response that is generated
by calling redirect, React Router will simply move
to that different route
to which you're trying to redirect.
So here we could path a path of slash here
to make sure that after this action was called,
we actually make React Router load a different route,
the route with the path slash,
which in this case is, of course, this route.
So that we leave the create-post route
and we move to this route instead.
That's how we can initialize this navigation action.
Again, all performed by React Router here.
There is no backend code involved here.
We're just sending a request to the backend
but this code here all runs on the client side
in the browser.
But with that all added, if we save everything
and we go back and go to the New Post area
and try to add a new post, like this,
you see I am redirected.
This new post shows up here.
If I reload, it still shows up here,
so it was indeed sent to the backend.
We can also see it in posts.json there.
And therefore, this proves that this action works
the way it should
and we're now using React Router for sending
and for fetching data.
And as a result, you can see that our components
got much leaner.
We don't have to manually keep track
of what was entered.
We don't have to manually handle the form submission
and prevent the default.
We have to do none of that.
We instead embrace React Router
for sending and getting data,
for navigation between pages,
and that's why React Router is awesome.
Also, for simple demos like this one,
but especially, of course, for more complex websites.