Search justacoding.blog. [Enter] to search. Click anywhere to close.

January 13th, 2023

Make A React Custom Hook For API Calls And Data Fetching

Custom hooks in React can be extremely useful, so it’s no doubt a good idea to gain some familiarity with regards to writing your very own hooks.

Let’s firstly go over the basics; what custom hooks are and how they can solve our problems. Then we’ll look at an example of how we can use a custom hook to fetch data from an API endpoint, with the necessary functionality taking place on click (not on the component’s initial load).

You can take a look at a working example over here: React Custom Hook for fetching API data. We’ll work through this specific example in the subsequent sections.

What are custom hooks in React?

Simply put, custom hooks are a way for you to extrapolate your logic out into reusable, modular pieces. These pieces can then be used easily and efficiently throughout your React codebase, as required.

Like components, hooks are simply plain JavaScript functions.

You will no doubt be familiar with at least some of the in-built React hooks that are available – such as useState and useCallback.

But let’s take a look at how to create a new custom hook. In this case, we’ll look at how to handle API requests and data fetching via our own hook.

Creating the useApi hook for fetching data

First thing’s first, we’ll want a new function – we’ll name it useApi. This is our custom hook. It’s good to follow standard practice here, and start our custom hook name with use.

Our components will make use of this custom hook to fetch data via web requests. On top of this, they’ll also receive the relevant information about the request as and when it happens.

So when the request is in flight (loading), that information will be readily available to the calling component, effectively cascaded down from our hook.

When the request succeeds and passes back data (or when it errors) the calling component will also be aware of this, too.

The custom hook looks like this:

const useApi = (url, onSuccess, onError, method = "GET", data = {}) => {
    const [loading, setLoading] = useState(false)
    const [responseData, setResponseData] = useState(null)

    const makeRequest = async () => {
        setLoading(true)

        let config = {}
        if (method === "POST" || method === "PUT") {
            config = {
                method,
                body: JSON.stringify(data),
                headers: {
                    "Content-Type": "application/json"
                }
            }
        }

        try {
            const response = await fetch(url, config)
            const data = await response.json()
            setResponseData(data)
            setLoading(false)
            if (onSuccess) {
                onSuccess()
            }
        } catch (e) {
     				console.error(e)
            if (onError) {
                onError()
            }
        }
    }

    return [makeRequest, loading, responseData]
}

That’s the custom hook in it’s entirety, but let’s break it down in more detail.

Breaking down the data-fetching hook

The first thing to note here is that this hook contains it’s own internal state:

const [loading, setLoading] = useState(false)
const [responseData, setResponseData] = useState(null)

And these pieces of state are cascaded down as return values from the hook, as part of an array, as demonstrated here:

return [makeRequest, loading, responseData]

When the hook is invoked or used within the relevant component(s), we thus have access to these individual pieces of data to use as we please.

As you can see, this kind of hook is a neat way to encapsulate some of the common API/data-fetching functionality that you’ll commonly be working with in React applications.

The loading state is handled within the hook, for example – and thus it can simply be referenced underneath, in the calling component.

It (the loading state) doesn’t need to be defined or maintained by the caller at all.

This is a good real-world example of one of the benefits of using custom hooks in general. The relevant functionality can be neatly extrapolated away from the callers, which will help keep functionality consistent and streamlined and hence lead to a more manageable codebase in the long run!

Once you are familiar with writing your own custom hooks in React, you’ll no doubt come across many other cases where a custom hook can be really beneficial.

Using the custom hook on click

In this case, it’d be most useful to execute our web request on click, not necessarily when the component loads.

For this, we can use the makeRequest function that’s passed down from our hook, as demonstrated here:

<button onClick={makeRequest}>Call API</button>

The request itself is a simple function, using the fetch API (but you can obviously use any kind of tool or library you’d like here). This simple function is passed down as one of the items we return from the hook, ready for us to invoke.

This makes it (the function, the fetch request itself) accessible directly in the calling component, on click.

With that in mind, it’s simply a matter of calling the function directly and seeing the relevant items in state (loading, responseData) dynamically update.

Using the fetch API to make API requests and fetch data

I won’t go into too much detail here, but it’s worth briefly stepping over how the fetch API works for those who are unfamiliar.

fetch is an asynchronous function – so it needs to be awaited (or alternatively, used in a promise) or you won’t experience the correct behaviour (see: pitfalls of async functions).

The endpoint of the API is passed in, as well as an (optional) object to configure the behaviour of the request in question.

Once the response is received, it’s converted to JSON – simply using the json method on the response object.

You’ll also notice that there’s a try/catch statement, this will capture any errors that take place during the request and gracefully handle them (we’d show a dialogue or message in the real world).

And that concludes all of the stuff we’ll need for a basic web request to our API. We can easily and efficiently fetch data using this approach, in a reusable manner — and this forms the very basis of our custom useApi hook.

Supplying some callbacks

With this kind of functionality, it’s often nice to provide the option to supply your own callbacks.

You can see an example of this with the handleSuccess and handleError functions.

const handleSuccess = () => {
    console.log("Success!")
}

const handleError = (error) => {
    console.error(error)
}

When we actually use this custom hook (which we’ll do next) we can invoke these pieces of functionality as and when required (if they’ve been supplied, that is).

Obviously in this example, handleSuccess is invoked upon a successful request.

Conversely, handleError is used when an error occurs.

handleSuccess would typically be used to display a dialogue or some other indication within the UI that the request was successful.

A general note on callbacks

When implementing custom functionality such as this hook, it’s important to consider the possible use-cases. Would it be useful for the caller to “do something” during the execution of your code? So before or after it, perhaps?

If so, allowing (optional) callbacks to be fed into the functionality could be a great idea.

Actually using the custom hook to fetch real data

This hook can be used as any other hook is used in React:

const [makeRequest, loading, data] = useApi(
    "https://jsonplaceholder.typicode.com/todos",
    handleSuccess,
    handleError
)

We simply pass in the required parameters (endpoint, successCallback, errorCallback) and destructure the returned array into individual variables.

It’s then simply a case of using these returned items as per your business requirement in the callers:

<h1>{loading ? "Loading..." : data ? JSON.stringify(data) : "No data yet"}</h1>

Here, I’m just attaching makeRequest to a button, and outputting either some loading text or the stringified response data, based on the status of the request in question.

Improving the custom hook

This is a bare-bones example, as you can see, so there a number of obvious improvements and enhancements that can be made.

Here are a few ideas with regards to improving our hook:

  • Allow full modification of the parameters passed to fetch. There are lots of options that can be passed in, and it may sometimes be useful to modify these provided options on a request-by-request basis
  • Provide a mechanism for the request to fire off upon initial load, immediately. You can of course do this directly in the calling component, though it may be more useful to update the hook to handle this use-case

As always with this type of functionality, once the basics are in place it’s simply a matter of making the necessary improvements and adjustments over time as per your own individual requirements.

The example provided is enough to get you started with basic data fetching from an API; but there are of course many more modifications and additions you can make yourself to the functionality!

Conclusion

I hope you’ve enjoyed reading this short article.

To conclude, we’ve stepped through a simple example of a custom hook implemented within React. In the example provided, we can use the custom hook to fetch data from an external API using a simple web request.

We’ve discussed how the general idea with custom hooks is to extrapolate your own functionality to modular, reusable pieces, and this has been demonstrated in the real-world example.

If you’d like to see more code examples (mainly React-specific at the moment), please do check out the Code Examples section!

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog