How to Customize the React useQuery Hook

With React's useState and useEffect Hooks, we've been able to query and display listings information the moment our Listings component is mounted. In this lesson, we'll create our very own custom useQuery Hook that will consolidate the pattern of creating a state property, using the useEffect Hook, and running the server fetch() function to make a query and state update when a component mounts.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL with a single-time purchase.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL
  • [00:00 - 00:16] We were able to take advantage of the use effect hook to achieve the desired result of displaying the listings information the moment our component mounts. We also discussed that though this works, maybe we can consolidate the state initialization and the fetching functionality within a custom hook of its own.

    [00:17 - 00:29] Custom hooks in React allows us to share logic between components and with best practice should always have the use keyword in its name. In addition, custom hooks can call other hooks as well.

    [00:30 - 00:41] Let's see the desired game plan of the custom hook we'd like to create. We'll label this hook the use query hook and we'll look to declare it at the top level of the component like so.

    [00:42 - 00:59] Since the query of our existing application doesn't have any variables, our use query hook will only accept a single argument, the GraphQL query that we'd want to make. The hook will return an object where we'll be able to destruct the data from and use in our components.

    [01:00 - 01:10] And just like we've done before, we'll make our hook accept a type variable to help type to find the data being returned from our query. And that's all the components will need to do.

    [01:11 - 01:20] The hook will take care of tracking a state property, how the server fetch function is to be called and when it should be called. In our case, when the component mounts for the very first time.

    [01:21 - 01:33] Now with an idea of what we intend to do, let's create this hook. In our existing lib API folder, we'll create the hook in a file called use query.

    [01:34 - 01:54] We'll export a function constant called use query that accepts a type variable that will label T data and will specify a default value of any. In addition, the function itself will accept a query parameter argument of type string.

    [01:55 - 02:10] We've used the use state hook before to maintain the query data kept in the components for it to be presented in the UI. We'll use the use state hook here similarly to keep track of the data to be returned from the query for us to return it in the end of our hook.

    [02:11 - 02:23] We'll also use the use effect hook to help run the effect of making the API call when a component is first rendered. So we'll import these hooks from react.

    [02:24 - 02:38] In the beginning of our custom hook, we'll look to use the use state hook to create a state object that will contain the data from our API call. Before we initialize this state property, let's describe the shape that it expects to take.

    [02:39 - 02:48] We'll create an interface at the top of the file called state. This state interface will contain the data object that will be returned from our API call.

    [02:49 - 03:00] The shape of data will be from a type variable this interface accepts, passed from the use query hook function. So we'll also label it as T data.

    [03:01 - 03:18] The shape of data will only be what the T data type variable is after our API call has been made complete. Theraphore will use a union type to declare that data could also be null, which refers to when before the API calls to be made.

    [03:19 - 03:31] With the shape of state in mind, let's use the use state hook and initialize this state object. We'll label the state property as state and the function setter as set states.

    [03:32 - 03:47] We'll pass in the state interface as the expected type value of the use state hook and we'll initialize our state data object as null. Cool.

    [03:48 - 04:00] If we want to make our API call, we'll need to call the server fetch function we've created. As a result, we'll import the server object into our file.

    [04:01 - 04:13] We'll now construct the server fetch function and specify that we'd wanted to run in an effect hook. We'll follow the usual recommended pattern and define an asynchronous function within our hook.

    [04:14 - 04:26] We'll call this function fetch API. The function will simply make the API request passing in the appropriate payloads and type variable.

    [04:27 - 04:41] It will also retrieve data from the request and update the state specified in our hook. Keep in mind that when we update the states, we're setting an object that contains the data object.

    [04:42 - 04:54] With the fetch API function declared, we'd essentially want to run the function upon first render only. So with that said, we'll run the function in the hook and specify an empty array in the dependencies list.

    [04:55 - 05:05] Ah, notice how the ESLint rule now throws an error? It asks us to pass in the query value to the dependencies list or remove the list entirely.

    [05:06 - 05:18] This is because query is being passed from elsewhere and is being used in the hook. As a result, the use effect hook wants to make sure we don't reference a stale value of query if our effect was to run again.

    [05:19 - 05:31] In this instance, we'll actually include it in the dependency. This is because we don't expect our use effect hook to run after initial render , since we don't picture the query value to ever change.

    [05:32 - 05:43] If we look at the listings file, it's actually being declared outside of the components. And if the component was to get re-rendered on changes, we'll still be referencing the same constant value.

    [05:44 - 05:57] This is one of the recommended approaches mentioned by Dan Abramoff. If we don't ever expect the dependency to change, keep it outside of the component, and as a result, there's no harm in placing it as part of the dependencies list.

    [05:58 - 06:10] At the end of our use query hook, we'll now simply return the state object. At this moment, our use query hook should function the way we expect it to.

    [06:11 - 06:20] We'll re-export the use query function from the API index file. We'll import it in our listings component.

    [06:21 - 06:34] We'll remove the listings state declaration, the use effect hook, and the fetch listings function from our listings component. The delete listing function depends on calling fetch listings again.

    [06:35 - 06:50] So we'll remove that line from the function for now, and come back to how we attempt to make a refetch happen shortly. And finally, we'll declare the use query hook at the top of our component, pass in the listings query we expect to make.

    [06:51 - 07:10] We'll specify the type of the data we want, and we'll retrieve the listings data itself from what the use query hook returns. And we now no longer need the use state and use effect hooks in our component file itself.

    [07:11 - 07:26] In this case, we should now check for what listings should be. If data exists, we'll use a ternary statement to say that the listings value in our component will be the listings property from the data object returned from our query.

    [07:27 - 07:40] If data doesn't exist, it will just simply be no. And there we have it. When we look at the UI of our application, we can see that the use query hook does as intended.

    [07:41 - 07:53] It makes the request the moment we load our application. Great. This is actually a pretty good example to see how sometimes custom hooks help make components look a lot more concise.

    [07:54 - 08:02] To the listings component, it's pretty much unaware of what the hook is doing. All it cares about is using the hook at the beginning and getting the data it needs.

    [08:03 - 08:20] If the property or information within data exists, it sets it to the property it's looking for and simply updates the UI to reflect that. If any other component now wants to follow this format, all they need to do is simply plug and play the use query hook and the hook takes care of everything else.

    [08:21 - 08:25] Amazing. With that being said though, there are some limitations to what we've set up.

    [08:26 - 08:37] One of them is we've removed the ability to refetch our query when a change has been made. So right now if we delete a listing, we don't get the capability of having our list be re-rendered to show that change.

    [08:38 - 09:13] So in the next lesson, we'll see how we can introduce the refetch capability as part of the use query hook. [ Silence ]