How to manage API calls in React

7 min read

React library is well known for building rich and highly scalable user interfaces. There are many ways to fetch data from an external API in React.

Before you go through this blog be familiar with React library and Application Programming Interface (API).

In this blog, we will discuss different ways to manage API calls in React. In the end, you will be able to choose the best approach based on the application requirements.

1. The Fetch API

Fetch API is built into most modern browsers on the window object (window.fetch) and enables us to make HTTP requests very easily.

The following code snippets show a simple example of using Fetch API in practice.

import { useEffect } from "react";

const fetchUsers = () => {
  // Where we're fetching data from
  return (
    fetch("http://www.abc.cd/test")
      // We get the API response and receive data in JSON format
      .then((response) => response.json())
      .then((data) => console.log(data))
      .catch((error) => console.error(error))
  );
};

The only goal of this function is to access the data and to convert the response into JSON using the response.json() method. Here, the use of the json() method is to get the response object which is stored in data and used to update the state of users in our application.

The fact that Fetch is promise-based means we can also catch errors using the .catch() method. Any error encountered is used as a value to update our error’s state.

Added to that, we make this request within the useEffect() hook with an empty dependencies array as the second argument so that our request is only made once, not dependent on any other data. Here is an example how to use it in useEffect() hook:

import { useEffect } from "react";

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

Isn’t this handy! Let’s see what other methods do 😃.

2. Axios Library

Axios is a Promise-based HTTP client for JavaScript which can be used in your front-end application and your Node.js backend. By using Axios it’s easy to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.

In this example, we have to first install Axios using npm or yarn and then add it as an import to your parent component.

npm install axios

The following code snippets show an example of using Axios:

import axios from "axios";

const fetchData = () => {
  return axios
    .get("http://www.abc.cd/test")
    .then((response) => console.log(response.data));
};

Similar to the Fetch API, Axios also returns a promise. But in Axios, it always returns a JSON response. The coding part is similar to the Fetch API, except for shorter steps and better error handling.

Check out the official documentation for better knowledge.

3. Async-Await syntax

Async/await is a relatively new way to write asynchronous code synchronously.

The async keyword before a function has two effects:

  • Make it always return a promise.
  • Allows await to be used in it.

The await keyword before a promise makes JavaScript wait until that promise settles, and then:

  • If it’s an error, the exception is generated.
  • Otherwise, it returns the result.

The following is the code snippets :

async function fetchData() {
    try {
      const result = await axios.get("http://www.abc.cd/test")
      console.log(result.data));
    } catch (error) {
      console.error(error);
    }
  }

When we use useEffect(), the effect function (the first argument) cannot be made an async function. For that, we can create a separate async function in our component, which we can call synchronously within useEffect and fetch our data accordingly.

4. Custom React Hook

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks. The idea behind custom hooks is to extract component logic into reusable functions.

So let's call our custom hook: useFetch. This hook accepts two arguments, the URL we need to query to fetch the data and an object representing the options we want to apply to the request.

Alright! Now, let's see how easy it is to fetch data with our useEffect() hook. We are going to use the Fetch API to make our request. For that, we have to pass the URL and the options we want to retrieve. From there, we get an object that we can use to render our application.

import { useState, useEffect } from "react";
const useFetch = (url = "http://www.abc.cd/test", options = null) => {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch(url, options)
      .then((res) => res.json())
      .then((data) => setData(data));
  }, [url, options]);
  return { data };
};
export default useFetch;

We can call whenever we need to fetch data inside our application.

import useFetch from "./useFetch";
const { data } = useFetch("http://www.abc.cd/test");
console.log(data);

5. React Query Library

React-query is a great library that solves the problem of managing server state and caching in applications.

"It makes fetching, caching, synchronizing, and updating server state in your React applications a breeze”

Firstly, let’s install the required package

npm install react-query react-query-devtools

Note: React Query also has its own dev tools which help us to visualize the inner workings of React Query.

React-query gives us a cache, which you can see below through the React Query Devtools. This enables us to easily manage the requests that we have made according to the key value that we specify for each request.

import React from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider, useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";

const queryClient = new QueryClient();

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <FetchData />
    </QueryClientProvider>
  );
}

function FetchData() {
  const { data } = useQuery("UserData", () =>
    fetch("http://www.abc.cd/test").then((res) => res.json())
  );

  return (
    <div>
      // data you want to show
      <ReactQueryDevtools initialIsOpen />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

In short, we just need to tell the library where you need to fetch the data, and it will handle caching, background updates, and refresh data without any extra code or configuration.

It also provides some hooks or events for mutation and queries to handle errors and other states of side effects which remove the need for useState() and useEffect() hooks and replaces them with a few lines of React Query logic.

For better insights check out the official documentation.

There are various other ways to manage data fetching such as SWR, GraphQL API, but this blog post is not actually explaining them in depth but you can check it out :)


That's all about it. By the way don't forget to check out the comment section of the following tweet. This might help you to choose the best approach.

Happy coding 😉. Thank you for reading my blog 💖.

Feel free to connect on Twitter :)