Building a Custom Fetch Hook in React
Tags: webdev, react, hooks
Published: 2023-04-03 23:31:20

by [Clinton Joy](https://blog.openreplay.com/authors/clinton-joy)

We are continually looking for ways to improve your code as React developers. Another viable way is to create and use custom hooks. Custom hooks allow you to package and reuse functionality across your application. This makes it easy to maintain and understand your code. In this article, we'll look at the power of React custom hooks and show how to create a custom hook (Fetch) that lets you get data from a server.

## What are React Hooks?
React Hooks are simply a set of functions that allow you to use state and other React features in functional components.

Custom React Hooks can be used to reuse logic in React applications. They allow you to package complex logic and reuse them across multiple components of your react Application.

Here are a few reasons why you might want to use custom React Hooks in your application:

**Reusability**: They can be reused across multiple components, reducing the amount of duplicated code in your application by following the DRY principle.

**Better organization**: Custom hooks can help you organize your code better by separating the logic from the components that use it. This helps prevent bloated codes that are difficult to maintain.

**Improved performance**: Custom hooks can help improve the performance of your application by reducing the number of re-renders caused by state updates. By managing the state better, you can avoid having to re-render things that don't need to be, which will make your application run faster overall.

## Building the custom Fetch Hook

The first step in creating this hook will be to create a folder called "hooks," within which we will create a file for the hook, which we will name "useFetch.js". You can name the hook whatever you want, but be sure to start with the word "use" This way, react recognizes the hook. 

In our just-created file, we will be creating a function that will take in an argument (`url`), and this `url` will be the API endpoint we are fetching the data from. Then we import the `useState` from `react` and use it to store the data we get from the server. Also, we `import` the `useEffect` to account for changes when the component that uses this hook evaluates.

```javascript
import { useState, useEffect } from "react";

const useFetch = (url) => {
  const [data, setData] = useState(null);
  useEffect(() => {}, []);
};
```

For this hook to be able to fetch data from a server, we need a function that fetches this data, right? Yeah, we will create an async function (`fetchData`) within the `useEffect` hook to fetch, `await` the response, and then get the JSON data and save it using the `setData` state. After that, we immediately invoke the function so that we can retrieve the data. Also remember that the `useEffect` hook accepts dependencies, which cause the code in the `useEffect` hook to rerun, so we'll add the `url` as a dependency so that anytime the `url` changes, the FetchData function will rerun.

```javascript
const useFetch = (url) => {
  const [data, setData] = useState(null);
  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
    };
    fetchData();
  }, [url]);
};
```

The last step in making a custom hook is to return the values that your components will need. In this case, we will return the data that was fetched. We will also add the export keyword to the useFetch function so that it can be imported into the component where it is needed.

```javascript
import { useState, useEffect } from "react";

export const useFetch = (url) => {
  const [data, setData] = useState(null);
  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
    };
    fetchData();
  }, [url]);
  return { data };
};
```

## Improving the Fetch Hook
We've succeeded in making a custom React hook that we can use in all of our React components to fetch data. I'd like us to improve the Fetch hook by handling the errors that can occur when fetching data from a server and by adding a loading state so we can add a  "loading" message while we wait for our data.

### Adding Loading message 
We must have had to wait for a page to load before we could access some information in a modern application. If you can remember, while we waited for the page to load, we saw a spinner or a message that said the page was loading. We can add this to our application by adding the functionality in our Fetch hook. To do this, we use the `useState` hook to create a loading state and set the loading state based on whether or not our data has been fetched.

```javascript
import { useState, useEffect } from "react";

export const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [isPending, setIsPending] = useState(false);
  useEffect(() => {
    const fetchData = async () => {
      setIsPending(true);
      const response = await fetch(url);
      const json = await response.json();
      setIsPending(false);
      setData(json);
    };
    fetchData();
  }, [url]);
  return { data, isPending };
};
```

As you can see above, we set isPending to true before the fetch and false after the fetch. We then returned the value of isPending so we can use it in any component that imports this hook.

### Adding Error handling
Error handling is very important when it comes to fetching data from a server, so let's add this functionality to our Fetch hook. To do this, we use `useState` and create a state for the error. Then in the fetchData function, we use the `try-catch` block to try a block, so if an error occurs, we will catch it and set the error message.

```javascript
import { useState, useEffect } from "react";

export const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [isPending, setIsPending] = useState(false);
  const [error, setError] = useState(null);
  useEffect(() => {
    const fetchData = async () => {
      setIsPending(true);
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error(response.statusText);
        const json = await response.json();
        setIsPending(false);
        setData(json);
        setError(null);
      } catch (error) {
        setError(`${error} Could not Fetch Data `);
        setIsPending(false);
      }
    };
    fetchData();
  }, [url]);
  return { data, isPending, error };
};
```

So, as seen above, in our `try` block, if the fetch response condition is not true, we throw an error. The `catch` block will immediately catch the error, and we will `setError` to a custom message that we will display to the user.
The code above is our complete custom Fetch hook with additional features; just by importing it, you can use it in any component to fetch data.


<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>



## Testing our Custom Fetch hook
I'll walk you through how to utilize our Fetch hook in our components. To achieve this, we will be using a package called json-server, which will enable us to generate an endpoint for our JSON file so we can fetch the data.

### Creating JSON data
Here are the steps to creating our JSON file:
1. We create a Data folder at the root of your application
2. We create a db.json file inside that Data folder.
3. Then we write our JSON data.

```json
{
  "Students": [
    {
      "id": "1",
      "name": "Clinton Joy"
    },
    {
      "id": "2",
      "name": "Jaja David"    
    },
    {
      "id": "3",
      "name": "Oyale Peter"
    }
  ]
}
```

So as seen in our JSON, we have a root resource called "student," which is an array of objects that hold all the data we will be fetching.

### Setting up JSON server
Installing the json-server package is required to access the API endpoint and utilize the JSON data, These are the steps to doing that.
* In our terminal we will use the `npm install -g json-server` to install json-server globally
* we will also run this command `json-server --watch ./data/db.json` on our terminal so we can get the API endpoint 

We should see a `url` similar to this one after our command has successfully run: "http://localhost:3000/Students."

### Fetching data with our custom Fetch Hook
To fetch data using our Fetch hook we need to create a component.
When we create a new react application in our SRC folder we get an App.js file. In this file, we will import our custom `useFetch` hook from the hook folder we created.
then we create a state that stores the `url`.

In our App function we can now call the useFetch hook and pass in the `url` as an argument.
if you can recall the useFetch function returns data, isPending, and error so we can destructure it and get the properties

```javascript
import { useState } from "react";

import { useFetch } from "./hooks/useFetch";

function App() {
  const [url, setUrl] = useState("http://localhost:3000/Students");
  const { data, isPending, error } = useFetch(url);
  return <div>app</div>;
}
export default App;
```

Now let's output some of the fetched data to our browser by creating a template for the data that we will see in our browser.

```javascript
import { useState } from "react";

import { useFetch } from "./hooks/useFetch";

function App() {
  const [url, setUrl] = useState("http://localhost:3000/Students");
  const { data, isPending, error } = useFetch(url);
  return (
    <div className="App">
      <h1>Names of Students</h1>
      {isPending && <div>Loading....</div>}
      {error && <div>{error}</div>}
      {data && data.map((name) => <p key={name.id}>{name.name}</p>)}
    </div>
  );
}
export default App;
```

Yes, that concludes the `App` component. The template is very basic and serves only to show how we use the data from the db.json file. However, keep in mind that both the data and the error were set to null at the beginning of the code. For this reason, we first check to see if the `data` is available before using it. The same goes for the `error`; we only release an error message when there is an error occurring. After the data has been retrieved, the `isPending` state becomes false, and the loading message automatically disappears. 

Here is a     GIF of the final result when all functionalities have been successfully implemented.

![-](https://blog.openreplay.com/images/building-a-custom-fetch-hook-in-react/images/OOjyEir.gif)

As you can see, a loading message shows, and when the data was successfully fetched, it disappears from the page and uses the data that was found in our JSON file to display the template we created.

## Conclusion
In conclusion, React custom hooks offer a way to reuse logic and create more maintainable and organized code in your application. By creating a custom Fetch hook, we demonstrated how we can fetch data from a server and add loading and error-handling functionality to our hook, making it more robust and efficient. Using custom hooks can help you save time and reduce the amount of boilerplate code in your application. I hope this article has given you a better understanding of the power of custom React hooks and how to create your custom hook to fetch data from a server.



[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)