If you took the first course, you learned about hooks through  useState and  useEffect . In this course, you saw how to use these hooks to make API calls. And in the last chapter, you discovered context and how to easily access it with  useContext . You now have quite a few tricks up your sleeve when it comes to using hooks in your React applications. đ
Ready to create your own hooks? You can do it!
I donât know about you, but the first time someone told me I could make my own hooks, I panicked slightly. I imagined working on the React codebase and creating an enormous file to run a custom hook.
Don't worry; itâs easy to create a custom hook: itâs simply a function that starts with âuse,â which extracts the reusable logic and can use other hooks.Â
Letâs see how this works in practice.
In chapter 1, you called the API for the Shiny project to get freelancer profiles and the questions for a survey.
Take a closer look at your code. Can you see some repetition? Thatâs totally normal â weâre going to try to pool all of that with a custom hook!
To do this, create a new file in  /utils and call it  /hooks . Then create an  index.jsx file.
For creating the hook, use the syntax  async / await :
import { useState, useEffect } from 'react'
export function useFetch(url) {
const [data, setData] = useState({})
const [isLoading, setLoading] = useState(true)
useEffect(() => {
if (!url) return
async function fetchData() {
const response = await fetch(url)
const data = await response.json()
setData(data)
setLoading(false)
}
setLoading(true)
fetchData()
}, [url])
return { isLoading, data }
}The codeâs quite clear, isnât it? For the new  useFetch hook, pass the URL of the API you want to call as a parameter. It has an internal state that lets it store data and know if data is loading with  isLoading .
In  useEffect , the hook carries out an empty  return if the URL parameter is empty, and starts by setting  isLoading to  true . It declares the asynchronous function  fetchData in order to:
Call  fetch .
Parse what is returned with  data.json() .
Change the state of  isLoading .
url is part of the  useEffect list of dependencies, which triggers the call if the URL passed as a parameter change. Then, call the function  fetchData .
To use the new hook, modify  /Survey/index.jsx . Start by importing your hook with:Â
import { useFetch } from '../utils/hooks'Then get the data with:
const { data, isLoading } = useFetch(`http://localhost:8000/survey`)
const { surveyData } = dataYou should also delete all of the content in  useEffect that would have performed the fetch . Deleting unnecessary code is incredibly satisfying, donât you think? đ
Donât forget to replace  isDataLoading with  isLoading , which will be more generic here, and replace  surveyData with  data .
Does it still work?
Uh oh. Thereâs an error that is stopping the code from running! đ
Itâs nothing to worry about! With navigation, we accessed the content of the question object with  data[questionNumber] . Except during set up,  data is an empty object, which means that with:
const { surveyData } = datasurveyData is undefined. Therefore, JavaScript generates an error for  data[questionNumber] . One way of avoiding the error is to check that  surveyData is defined before using it in the component:
<QuestionContent>
{surveyData && surveyData[questionNumber]}
</QuestionContent>The page once again works like before using the  useFetch hook, which means a lot of repetitive code was deleted! đ
But what happens when the API returns an error? The app wonât behave the way you want it to, and your user wonât have any idea whatâs going on. Itâs a good idea to integrate error handling in the  useFetch hook so that any problem appears on the screen.
To do this, create a state for  error in  utils/hooks/index.jsx with:
const [error, setError] = useState(false)Then add a try and a catch to the  useFetch hook:
export function useFetch(url) {
const [data, setData] = useState({})
const [isLoading, setLoading] = useState(true)
const [error, setError] = useState(false)
useEffect(() => {
if (!url) return
setLoading(true)
async function fetchData() {
try {
const response = await fetch(url)
const data = await response.json()
setData(data)
} catch (err) {
console.log(gerr)
setError(true)
} finally {
setLoading(false)
}
}
fetchData()
}, [url])
return { isLoading, data, error }
}That lets you pass  error to  true whenever you run into a problem.
Similarly, in  Survey.jsx , you now get error:
const { data, isLoading, error } = useFetch(`http://localhost:8000/survey`)And you can just add the following above the return:
if (error) {
return <span>There is a problem</span>
}Brilliant! Youâre even able to handle API call errors! đ
With this knowledge in hand, use  useFetch to send user responses and get the API results from the  /results page.
Watch how to do all of this in the screencast below. đ
Will I be able to use  useFetch in all of my codebases in production?
This  useFetch hook is excellent for avoiding repetition and practicing how to create custom hooks. However, in reality, this code is a little too basic to be used in production. Instead, you could use a much more robust tool such as React Query, which will let you use hooks to make queries and cache them in your applications.
A quick reminder: hooks have their own set of rules.
Hooks are only accessible in a React function component, meaning that it is not possible to use them in a class component or a simple JavaScript function.Â
Call hooks at the level of your component root.
Be careful when naming your custom hooks. Even if this is a convention rather than a strict rule, your custom hooks should start with  use so you can immediately identify them.Â
For now, here are a few hooks that youâre likely to come across in different codebases:
Read the documentation if you're interested. Although the  useRef hook has several uses, itâs mainly used for interacting with DOM elements.
useReducer lets you better manage your state when it includes lots of properties that require periodic modification.
These two hooks allow you to avoid redoing calculations that might impact performance. You can specify the values for which you need to redo calculations only if one of the parameters changes, thanks to  useMemo and  useCallback .
And there are more! React is currently on version 17 (at the time this course was written). However, they could have created newer hooks in the meantime.

Now that youâve created your custom hook for making API calls,  useFetch , you can use it for the  /Freelancers and  /Results pages.
Youâll also have to create a personalized  useTheme hook that gets the context from the theme and lets you get the current theme ( dark or  light ).
From there, you can integrate the style changes for the theme and then integrate the  /results page as per the prototypes if you want to challenge yourself (or get the CSS from the solution branch if youâd prefer to spend less time on the CSS).Â
As usual, youâll find the exercise on branch P2C3-begin and the solution on P2C3-solution.
A custom hook is a function that starts with  use , extracts the reusable logic, and can use other hooks.
The rules of hooks also apply to custom hooks, as does the convention of naming your custom hook  use ...  .
Other hooks exist such as  useRef ,  useReducer ,  useMemo , etc.
Your app is starting to look pretty good! I hope that youâve enjoyed taking a closer look at hooks. But how can you be sure that youâre not going to break everything if you make changes in 6 months, or even a year, when you no longer remember how your code is implemented? Through tests, of course, which is the topic of the next part. But before moving on, itâs time to test your knowledge with a quiz. Best of luck, and see you in a bit! đȘ