Now with routing added,
we added another crucial feature to this application,
a feature which is especially important
in more complex web applications
you might be building with React.
And React router, at least if you have version 6.4
or higher installed, also offers some amazing features
that go beyond picking the right component
for the right path.
Instead, React router, when using version 6.4 or higher
also helps with data fetching and data submission.
And that's now the last part we're also going to explore
in this crash course.
Because of course here we are fetching data,
in the Posts component, we have the PostsList component,
and in there, we are fetching data.
We have this useEffect hook here
where we load our posts and set our posts.
We also have to code for sending postData here
but that code isn't used at the moment anymore
and I'll get back to that later.
But when it comes to fetching data,
we could stick to useEffect.
But when using React router version 6.4 or higher,
we have a more convenient way of doing so.
Because with that version,
you can go to your route definitions
to the route that needs data,
so the Posts route here, for example,
and you can add a loader property to that route definition.
Now the loader property
expects to get a function as a value,
and React router will execute that function
whenever that route gets activated,
so whenever it's about to render this element.
That's when this function gets executed
and you can therefore use this function
to load and prepare any data that might be needed
by this route component
or any other component used as part of that route,
so any nested component
used inside of the Posts component, for example.
Now therefore, we could add the data fetching logic
for sending that HTTP request here.
But to keep this main JSX file lean,
it's more common to go to your route component file,
so in this case, the Posts.jsx file,
and there, export an extra function,
which typically is called loader
though the name is up to you.
But loader is a common convention
since this will be the function
that will be assigned to that loader property
in the route definition.
Now that loader function here
still executes on the client side.
It's still code that executes in the browser.
And therefore, in there, you can do whatever you wanna do
that could be done anywhere else in this app as well.
Here I wanna fetch my posts.
And therefore, I'll go to the PostsList component
and copy these two lines of code here.
The state updating code does not matter here
because this loader function
executes outside of the component
and can't manipulate component state.
You will still see how we then get the posts
into the component a little bit later.
So I cut the code for fetching data,
and back in Posts.jsx,
I'll paste it into this loader function.
Now since I used the await keyword here,
we should add async
in front of that function to unlock that keyword,
and we can't do that
because this function which will be assigned
to this loader property can be an async
or a synchronous function.
It may return a promise or it may not.
React router does not care about it.
But if it does return a promise,
React router will wait for that promise to resolve
before it then renders this route component,
so this element which you specified here.
And therefore here we can use async
and then send our request to fetch all posts
and extract the data.
And then, that's the important part,
we can return the data that we wanna expose
to the element that is rendered for the active route.
So in this case, resData.posts.
I return that as part of this loader.
And then in main.jsx, we can import this loader
by editing this import here
where we import the posts component function.
And there, we can also import this loader,
and I'll give it an alias
since we might have more loaders from different routes
and we don't wanna have name clashes.
We could name this postsLoader,
but of course this name is up to you.
So in the end, it's now this loader function here
which is assigned as a value
for this loader property in this route definition.
And therefore, React router will execute this function
and wait for the promise to resolve
before it then renders this element.
And the data we return here in this loader
is actually exposed to this element.
React router will make sure that the data returned here
can be accessed in this route element
or in any nested component,
so also in postsList, for example,
that's what we can go to postsList.
And there, we can now import
and never hook from react-router-dom,
and that would be the useLoaderData hook.
Now this hook can be called
inside of the component function.
Like all Hooks, it must only be used
inside of component functions in the end
and that gives us the data that is returned
by this loader that's assigned to this route
which is the route that is active
when the postsList component is rendered.
So here I get my posts in the end
because in my loader here, I'm returning the posts.
So that's what I get here with help of use loader data.
Therefore, we can get rid of that posts state.
We can also get rid of the IsFetching state
because the loading state of this route
and this route component will be handled differently soon.
And we can also get rid of this entire useEffect call here
because we don't useEffect for data fetching anymore.
Instead, we use this feature provided by React router.
We therefore should also remove these imports
of useState and useEffect.
And then down there in the code,
we no longer have this isFetching condition
because we no longer have this isFetching state,
so that should go away.
And we also don't need to check for isFetching here anymore.
I do, however, still want to check
for whether we have posts or not.
And posts is now simply coming from use loader data
which gives us the data returned by the loader
that was called for this route
which is active at the moment.
With that, if we save this,
if I reload, you see nothing for a short period
and then it actually loads the page and shows us the posts.
And we see nothing initially
because now the behavior is
that as long as the loader executes,
this route element isn't rendered.
So React router waits for the loader to finish
before it renders this element.
And actually here, this even means
that nothing is rendered on the screen
if we first navigate to this page.
If I instead navigate to this page after closing this page,
it actually executes this behind the scenes
and therefore then we don't get stuck
and have an empty page for a brief moment.
But if we visit this page for the first time,
we see nothing initially until everything has been loaded.
Now, this might not be the behavior you want
and React router also gives you ways
of showing pages earlier
and not waiting for the loader to finish.
And I do dive into more advanced React router features
that help you in such situations as well
in the complete guide course,
but it's a bit beyond the scope of this crash course.
The important takeaway here simply is
that this loader is executed
before this element is rendered.
And whilst this might not be ideal
in case of a slow back and as we have it here,
it's a great solution
for all cases where you don't have a slow backend.
Because if I go back to my backend code
and comment out this timeout here
so that we don't have this slowdown on the backend anymore
and I then restart this backend server,
you now see that it loads pretty much instantly
if I reload this page.
And therefore now we have a great user experience again
but we have way less code that must be written
inside of the component
since we're using this loader feature.
And again, for situations with slow backends,
there also would be solutions.
And therefore you can handle all these scenarios
with less code, which is of course great
since it decreases the complexity of your app
and means less work for you as a developer.