Ok. So, we set up React Query in our application.
Now, let's see how to use it to make an API request.
And we'll start by our NavBar component,
where we fetch the user data.
What we're going to do is replace
the "useState" and "useEffect" calls
with a single call to "useQuery",
that's a hook provided by React Query.
The way this works is that we pass a "queryKey",
that is a name used to identify this query.
And we're free to choose whatever name we like,
but this will be used when caching the data
so it helps if it's a name that describes the data.
That's why in this case I'm calling it "user".
"useQuery" expects a second argument
that is a function that will actually
go and fetch the data.
So in this case it will be similar to the code
we already wrote for "useEffect".
However in this case we need to return the user data,
so it will be managed by React Query.
Now, if there's an error,
in this case we're treating an error response
to mean that the user is not authenticated.
So we could explicitly return "undefined" here,
because that's the value we want to assign
to the "user" data.
Ok. Now, "useQuery" returns an object
that I'll call "query",
and this object has a number of properties,
But all we need here is the "data",
that in this case is our "user" object.
Ok. At this point we can remove the old code.
But now we have a small problem in that
"setUser" doesn't exist any more.
Let's just comment this out for the moment,
and we'll come back to this later.
Since we deleted the code
using "useEffect" and "useState"
we can remove this import as well.
Ok. Let's go and test our application
to see if it still works.
Let's refresh the page, to start from scratch.
If you notice, in the console logs,
when the NavBar is rendered
"user" is "undefined" at first,
and then it gets updated
with the data returned by the API.
So this works exactly like it used to.
Now, let's look at the network requests.
Here we can see that it made a request
to our "user" API route.
Now, if we navigate to another page,
it makes another "user" request.
And if we go back to the HomePage,
again it re-fetches the user data.
So it's still making a new request every time
and not caching the data.
In fact, with React Query
it makes even more requests.
If I put the focus on the Dev Tools window,
and then go back and click on the page,
this triggers a new "user" request,
which is quite surprising at first.
But of course there's an explanation for this.
Let's look at the React Query documentation.
If we search for "useQuery"
we can find the right page.
When we call "useQuery" we pass a "queryKey",
a query function to fetch the data,
and then an object that can have
many configuration options.
So it's important that we configure
our query appropriately.
The first two arguments are
the "queryKey" and the query function,
as we've already seen.
But if we look at the various options
there is one called "stateTime".
This is what we can use to cache the data.
By default, "staleTime" is zero,
which means the data will be re-fetched every time.
That explains why at the moment
we see all those "user" requests.
Now, "staleTime" is not to be confused
with this other option called "cacheTime".
"cacheTime" specifies how long the data
should remain in memory,
so it's about memory management.
If you request lots of data,
like, in our website, if we were to fetch
the data for each product page,
then after a while, when the user
is no longer looking at that product
we could remove its data to free up memory.
But "staleTime" is actually
the most useful option,
because that's what tells React Query
how long the data is valid for.
We'll soon see how to use it in our code anyway.
Now, there's another option called
"refetchOnWindowFocus",
and this defaults to "true".
So this is what's causing our user data
to be fetched whenever we focus on a different window
and then go back to the browser page.
The idea is that
if the user switches away from our website
and then comes back after some time
we may want to refresh the data
to make sure it's up to date.
This makes sense if the data changes frequently,
like in your Facebook or Twitter feeds for example.
But it's also good in our case,
because the JSON web token can expire,
so if the user goes away from our website for a while
when they come back we may want to check
if the token is still valid
by calling the "user" API route again.
Anyway, let's go back to our code
and set some options by passing
a third argument to "useQuery".
This is an object that can contain
any of the settings we've seen in the documentation.
But let's start with "staleTime",
that as I mentioned
is the most important one for caching.
The "staleTime" value is a number of milliseconds.
Let's set this to "30_000" milliseconds for example.
This means the user data will be considered "stale",
or "old", after 30 seconds.
In other words, it will be valid for 30 seconds,
and expire after that.
Let's see what difference this makes
to our application.
When we first load the page
as usual it will make
a first request to the "user" route.
Now, if we go to a product page,
this time it doesn't make any additional request.
And same thing if we go back to the HomePage,
or in fact if we focus on a different window,
and then put the focus back on the page.
You cans see that there's still
only a single request in the Developer Tools.
So what happens is that
the NavBar component is mounted on every page,
and this calls useQuery,
but there is already some data
in the React Query cache for the "user" query,
so "useQuery" returns the cached value,
without making a new API request.
Now, if we wait 30 seconds however,
so that the cached data will become "stale",
even after 30 seconds there's still
a single "user" request".
So it's not like React Query will fetch new data
automatically after exactly 30 seconds.
But if we open another page now,
this will trigger another call to "useQuery",
and this time it does make another API request,
because the old data that's in the cache
is now "stale".
But after that it will not make any request
for another 30 seconds (at least).
So that's how caching works,
and it's one of the main advantages
of using React Query.
Now, we saw in the documentation that
there's another option called "cacheTime".
But this doesn't tell
if the data in the cache is valid or not,
it only says when this data should be
removed from the cache to free up memory.
In this case we're fetching the "user" data
that's displayed on every page of our website,
so we always need this data.
We can tell React Query to keep it forever
by setting "cacheTime" to "Infinity".
Where Infinity, as you probably know
is a special numeric value in JavaScript.
Now, we can't really test "cacheTime".
But let's test what happens if we are signed out.
And I'll have to reload the app
for the state to update,
because of that "FIXME" comment
in the "handleSignOut" code,
that we'll fix later on.
If we look at the network requests,
the "user" route returned 401,
as expected since we're not authenticated.
But if we look at the Console messages,
here we can see that in the NavBar
the "user" variable was first "undefined",
then the request to "/api/user" failed,
and as a result the NavBar was rendered again,
but with "user" still set to "undefined".
If we go back to the network requests,
we should see that
even if we navigate to different pages
it doesn't make any additional request.
So it is caching "undefined" as the value
for the "user" query.
Which is exactly what we want.
So this is how the "useQuery" hook works.
We pass a key that's used when caching the data,
a function that actually does the fetching
and some options to configure
the behaviour of the cache,
and possible other settings.
"useQuery" returns an object that includes the "data".
And that "data" could be
freshly fetched from the server,
or come from the cache.
React Query manages that for us
depending on the options we passed in useQuery.