Back

Building a Custom Fetch Hook in React

Building a Custom Fetch Hook in React

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.

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.

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.

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.

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.

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.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

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.
{
  "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

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.

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.

-

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.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay