Creating a custom hook for fetching data in your React applications

Creating a custom hook for fetching data in your React applications

Stop re-writing the same fetch code in all your components


4 min read

Creating a simple hook to handle API data in your React applications


In this post we are going to explore custom hooks in React and then build one to fetch data from an API.

πŸͺ What are custom hooks in React?

Most simply, and directly from the React docs,

"Building your own Hooks lets you extract component logic into reusable functions."

For our example let’s say that our app has some data to fetch from an API. We could copy and paste some fetch logic into our components wherever we need to retrieve data but it wouldn’t be ideal. Custom hooks allow us to remove duplicated logic from our components. That way, once we’ve extracted the logic to a custom hook, we can just use it!

For example, the useAPI hook we're creating today can fetch quotes from the beloved "Parks and Recreation" character Ron Swanson and displaying them as a heading element like so πŸ‘‡

Screen Shot 2022-05-21 at 9.54.48 AM.png

Then in a separate component we could use the same hook to call another API route without needing to rewrite much code 😎

Screen Shot 2022-05-21 at 10.18.26 AM.png

πŸ›‘ Important to remember about React hooks πŸ›‘

You have to name custom Hooks starting with β€œuse”.
This convention is very important. Without it, React can’t automatically check for violations of Hook rules because it can't tell if a certain function contains calls to Hooks inside of it.

Two components using the same Hook DO NOT share state.
Custom Hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), but every time you use a custom Hook, all state and effects inside of it are fully isolated.

Fetching & Displaying Data Without Hooks

One of the most common things we need to do in our applications is to grab data from a server. On top of that, while it is being fetched from the server, we also need to show the user a loading screen. This logic we will generally need again at various other places in the app.

Generally when fetching some data from an API endpoint we will:

  1. Show a loading screen/component.
  2. Call an API.
  3. Receive some data.
  4. Stop loading.
  5. Render/use that data.
// App.js
import { useEffect, useState } from "react"

const App = () => {
  const [loading, setLoading] = useState(true)
  const [data, setData] = useState(null)

  const fetchApi = () => {
    .then(response => {
      return response.json()
    .then(json => {

  useEffect(() => {
  }, []);

  if(loading) return <h1>Loading</h1>

  return <div>
    <h1>Data fetched successfully.</h1>

export default App;

πŸͺ Now Fetching & Displaying Data With Our Custom useAPI Hook

Extracting the fetch logic to a custom hook allows us to reuse it easily in all of our other components and greatly reduces the lines of code our components need to consume data. In this case we got rid of almost half the code in our ./app.js file πŸ‘

import Loading from '../components/Loading';
import useAPI from '../lib/hooks/useAPI';

export default function App() {
    const {loading, data, error} = useAPI('/api/hello')

    if (error) {
        return (<p>{JSON.stringify(error, null, 2)}</p>)

    else {
        return (
            loading === true
                ? <Loading />
                :  <h2>{JSON.stringify(data)}</h2>

Writing our hook πŸͺ

To keep things organized we'll create a directory to house our custom hooks ./lib/hooks and we'll create our useApi hook under the file name ./lib/hooks/useAPI.js.

Breakdown of our hook
We'll get in the basic bones of our hook first. Where we create and export our "use" function and determine the state it will be returning. (Below) You can see we're creating a "useAPI" function that will return {loading, data, error}

import axios from 'axios';
import { useEffect, useState } from 'react';

const useAPI = (path) => {
    const [loading, setLoading] = useState(true);
    const [data, setData] = useState(null);
    const [error, setError] = useState(null);

    return {

export default useAPI;

Making the call πŸ“ž
Now we add a makeCall() function and use that to set the {loading, data, error} state in our hook, and then call that function in a React.useEffect with an empty dependency array. The makeCall() function we add will set our state variables for our hook to return, and that's our custom hook!

The Whole Hook

Wrapping Up

Custom hooks allow us to extract our common component logic into reusable functions, and we can write them to do just about anything. In our case we created a "useAPI" hook to consume data plus handle loading and errors.

You can find the code for this hook here


Did you find this article valuable?

Support Michael Curran by becoming a sponsor. Any amount is appreciated!