Building custom hooks in React to fetch Data
Tags: react, webdev, javascript, tutorial
Published: 2021-09-04 08:02:10

Fetching data from the backend is one of the crucial parts of the web application. For every application to work dynamically, it fetches the data from the server and then displays it in the user interface.

We retrieve the data using the API call and using built-in React hooks like useState, useEffect, and useReducer, the retrieved data is set to the state variable. It is then used in components for showing it in the view.

Before jumping into the code of how's it's done, let's first look at what React hooks are and why it is used.


## What is React Hooks

React hooks were first introduced in React 16.8. They are functions that let you hook into React state.

Some of the built-in hooks provided by React are useState, useEffect, useContext, useReducer, useRef, useCallback, and useMemo.


## Why React Hooks are used

One of the main advantages of using React hooks is the re-usability of logic. The hooks can be used in multiple components where we have to use a specific function.

It also makes the code more readable, efficient, and easy to maintain.

The normal code for fetching the data from the server and updating in the component is shown below

```
export function Home(){

	const [data,setData] = useState(null)
	const [loading,setLoading] = useState(false)
	const [error,setError] = useState(null)
	
	useEffect(()=>{
		(
		async function(){
			try{
				setLoading(true)
				const response = await axios.get('http:localhost:4000')
				setData(response.data)
			}
			catch(err){
				setError(err)
			}finally{
				setLoading(false)
			}
		}
		)()
	},[])
	
	return(
		{loading && <div>Loading...</div>}
		{data && <div>{data}</div>
	)
}
```


We write the logic inside the useEffect hook to update the state properties like data, loading, and error.

While it's perfectly fine to write like this, what if we want to do the same kind of thing in multiple components where we have to fetch another data.

We have to rewrite all of these codes multiple times in all of those components which is not very efficient and hard to manage.

In big codebases, it is better to follow the Don't Repeat Yourself (DRY) principles, that is, it's better to write code once and make it reusable instead of writing it again and again in multiple components.

That's where the real magic of Custom Hook is. We can write the code in a separate js file and call it with URL from all the components that might need to fetch the data from the server.

This makes the code efficient and easily maintainable.

Like useState and useEffect have their function, we create custom hooks by combining them for a specific ability.


## Creating custom useFetch hook

We first create a new javascript file with the name useFetch.js.
The name of the hooks starts with use as a part of react hooks convention.

![useFetch javascript file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8ipnu7lhb7tmd0mcfu1.png)


Inside the file, create a new function with the name of the hook. The difference between React hook and a React component is that hook doesn't return JSX. It only returns the state variable or function that you want to use in a component.

```
export function useFetch(){

}
```


To make an API call, use a useEffect hook because it will trigger the API call function inside it when rendered. Here, the API call is made using Axios.

The API Url that needs to be called is passed to the hook as an argument from the component.

```
import { useEffect } from "react"
import axios from axios

export function useFetch(url){
   useEffect(()=>{
      (
         async function(){
            const response = await axios.get(url)
         }
      )()
   },[url])

}
```


Usually, we have 3 state variables that are data, error, and loading created using useState to store the response data, error and loading respectively,

If the data is received, we set it to the data variable. If not, the error message will be set to the error variable.

Loader is initialized as false. When the API is called, it is set to true so that a loader component can be loaded in the view.

At the end of the API call, this loader is set back to false by using the finally block.

```
import { useEffect, useState } from "react"
import axios from "axios"


export default function useFetch(url){

    const [data,setData] = useState(null)
    const [error,setError] = useState(null)
    const [loading,setLoading] = useState(false)

    useEffect(() => {
        (
            async function(){
                try{
                    setLoading(true)
                    const response = await axios.get(url)
                    setData(response.data)
                }catch(err){
                    setError(err)
                }finally{
                    setLoading(false)
                }
            }
        )()
    }, [url])

    return { data, error, loading }

}
```


The only dependency we're going to put in the useEffect dependency array is Url because if the Url changes, we have to request new data.

That's basically for useEffect. Now we return the states that are created inside the hook as an object.


## Using Custom Hook in the Component

Inside the component, import the useFetch hook from its javascript file. After importing, call the hook with the API Url as an argument.


### Fetching data using custom Hook

```
export function Home(){
	const {data,loading,error} = useFetch('https://localhost:4000')

        if(error){
           console.log(error)
        }
	
	return(
		{loading && <div>Loading...</div>}
		{data && <div>{data.map(item => <div>{item}</div>)}</div>}
	)
}
```


In addition to this, we can also customize the hook by making it return any function that can be called from the component.

For example, we can create a refetch() function inside the hooks that re-fetches the API when called.

This function can be returned from the hook and can be called from the component.