Custom useQuery and loading/error states
In this lesson, we'll address how we can handle the loading and error state of our GraphQL queries with our custom useQuery Hook.
useQuery Hook works as intended, we haven't taken into account the tracking of the loading and error states of our queries.
We'll first address the loading state of our request. When we say loading, we're essentially referring to being able to track the status of our asynchronous request. If the request is in flight, the UI should reflect this with a loading indicator of sorts. And when complete, we should be presented with the expected data.
To keep track of loading, we'll introduce a new loading field into the
State interface of the state object tracked in our custom
useQuery Hook. We'll declare the type of the
loading field as
We'll initialize the
loading value as
false in our state initialization.
At the beginning of the
fetchApi() method within the memoized
fetch callback, we'll set the
loading state to
true while also specifying that our state
data is still
null. When a request is complete we'll set
loading back to
We've already used the spread syntax to return everything within
state at the end of
useQuery. We can now destruct the
loading property from the
useQuery Hook used in the
loading is ever
true in our
<Listings> component, we'll render a simple header tag that says
loading is set back to false, the title and the listings list is to be shown.
We'll ensure both the Node server and React client apps are running.
And in the browser, we'll now notice a brief
'Loading...' message when the query request is in flight.
We'll now address what would happen if our
server.fetch() function was to error out since our
<Listings> component isn't currently prepared to handle this.
With Apollo Server and GraphQL, errors can be a little unique. Oftentimes when a query has failed and returns an error - our server may treat that query as successful since the query request was made successfully.
Let's see an example of this. We'll briefly dive back into out Node server application and take a look at the
listings resolver function within the
listings resolver simply returns all the listing documents from the database collection we've set up in MongoDB Atlas. We'll temporarily throw an error before the
return statement of the resolver function to mimic if an error was to occur.
We can refresh our browser to attempt to query the
listings field again. We're not going to get the information we're expected from the API but if we take a look at our browser's
Network tab and find the post API request made, we can see that the request made to
/api was successful with status code 200!
If we take a look at the response from the API request, whether through the browser
Network tab or GraphQL Playground, we can see the server returns
data as null and an
errors array is populated.
When an error is thrown in Apollo Server, the error gets populated inside the
errors array that contains information about the errors added by Apollo Server. This complies with the GraphQL specification - if an error is thrown, the field should return data of
null while the error is to be added to the
errors field of the response.
On this note, we should specify that the response from the server can return an
errors field along with a
errors field from our server is an array where each item has a few different properties to display different information (
locations, etc). In the
src/lib/api/server.ts file, we'll create an interface for what an error would look like when returned from the server. We'll create an
Error interface and keep things simple by stating
message as the only property we intend to access from an error.
In our return statement from the
server.fetch() function, we'll state in our type assertion that the returned object will contain an
errors field of type