Our First Custom Hook: useDepartments
We'll write our first custom hook to fetch department info for our application in one centralized place.
Custom hooks may sound intimidating at first, but
useDepartments should change your mind#
If you look through our code, one thing you might notice while checking out the various components is that there are duplicate API calls happening: multiple components call the same API endpoints in our app to retrieve information.
Situations like this are a perfect opportunity to refactor some code into a custom hook and simplify the code contained in our components in the process.
In this lesson, we'll make our first custom hook. We'll learn how to extract code from one component and recreate it in a centralized and reusable hook.
Just like with our previous module lessons, we'll start with a simpler custom hook and work our way up to the more complex ones in future lessons.
Sample code zip file
If you need a copy of the sample app before we begin adding custom hooks, you can download it here.
We make duplicate calls to the department API#
The first duplicate API call that stood out to me is the
getAllDepartments call. It's called in both the
<ProductList> and the
<ProductForm> components after our updates to these components in the last module.
This seems like as good a place to start as any — let's make this call into a custom hook that we can share with all the components that need it.
Create a new
To keep our project organized, let's make a new folder inside the
client/src folder named
This is where we'll hold all the shared React Custom Hooks that our app will utilize.
Make a new
hooks/ folder exists, we can create a new file inside of it called
Following the "Rules of Hooks", which we covered in the module introducing hooks, we name all custom hook files by starting with the word
useXyz.js, and since this hook is about fetching departments, we'll name it
Okay, we're ready to start adding code to this hook now.
Review the code currently fetching departments#
As I said before, there are two components in our app that are calling the department API: the
<ProductForm> and the
<ProductForm> calling the
getAllDepartments API call currently looks like this.
<ProductList> calling the same API endpoint currently looks like this.
First, a snapshot of the component's state (this one has quite a few more state variables):
And the actual
useEffect fetching the departments.
There are some differences between these two components, but not so many we can't make a hook that can work for both situations. We'll just take it step by step.
Compose the hook#
Looking at the code above, we can see both API calls have a few state variables in common, in addition to the API call itself: they both share the
error state variables.
So let's define our hook function, import the
useState Hook into our file, and add those two state variables inside of it.
So far, so good.
fetchDepartments function into the hook#
After that, the next piece of code to add is the
fetchDepartments call inside of the
You can lift and shift the entire
useEffect out of the existing
<ProductForm> with almost no changes and paste it into the hook.
Pop this code into the hook underneath where we just defined the variables. That code should look like this inside of
Okay, we're making good progress.
setLoading and return
errors from the hook#
The next thing we're going to need to do is remove the
setLoading state in this function. When you look at the two components calling the department API, both have a
loading variable that displays some sort of loading spinner until the data's been fetched and transformed.
Once the data's returned, the loader state is set to
false, which allows the component to render with the data it retrieves (or the error message it displays if it's failed).
Now, you may be thinking, "Why don't we just declare a
loading state variable inside of this hook as well?" We could. That could work, but in each component, that same
loading state is used in multiple places inside the component — not just for this API call.
In this case, it makes more sense to remove the
setLoading variable from this hook and let the
loading state be handled in the components themselves.