How to make API calls in React

How to make API calls in React

A good API call requires more than just making a network request, getting the data and using the data. There are more subtle things that need to be put in place to provide a good user experience to your customers.

The code below illustrates how you most probably currently make API calls

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

function App() {
  const [data, setData] = useState([]);

  async function fetchAll() {
    await axios("", {
      method: "GET",
    }).then((res) => {
      setData(res.data);
    });
  }

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

  return <div></div>;
}

export default App;

If this is you or if this is close to how you make your API calls, then this article is meant for you.

What is wrong with the previous approach

  • There is no way to tell if this request is currently running if it has ended or is in an error state.

  • The request can't be cancelled. Yes, the request can't be cancelled. It is important to always make sure that most if possible all of your network requests are cancelable this helps improve the performance of your application by enabling you to free up network resources when they are no longer needed.

some possible scenarios where we may want to cancel a network request are

  • when a user navigates from a page or reloads a page

  • when implementing a file upload feature and you need to cancel the upload

  • when implementing a search feature and you want to cancel the previous network request when a user makes a new request

hopefully, you get the point behind the purpose of canceling a network request

Improving our approach

  const [loding, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const controller = new AbortController();

firstly, we add a loading and an error state alongside an abort controller. The loading state helps us know if our request is currently running and the error state tells us if it is in an error state. The abort controller is responsible for canceling the request.

  async function fetchAll() {
    setLoading(true);
    await axios("", {
      method: "GET",
      signal: controller.signal,
    })
      .then((res) => {
        setData(res.data);
        setError(null);
      })
      .catch((err) => {
        setError(err.message);
      })
      .finally(() => {
        setLoading(false);
      });
  }

secondly, we update our asynchronous function to update the loading state as soon as the function starts running and after the network request is done. We pass the abort controller signal to the network request. This will help us cancel the request if we ever need to later in our application.

  useEffect(() => {
    fetchAll();
    return () => controller.abort();
  }, []);

lastly, we need to add a clean-up function to our useEffect hook that cancels the network request anytime the component re-renders.

In the end, our component should look like this

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

function App() {
  const [data, setData] = useState([]);
  const [loding, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const controller = new AbortController();

  async function fetchAll() {
    setLoading(true);
    await axios("", {
      method: "GET",
      signal: controller.signal,
    })
      .then((res) => {
        setData(res.data);
        setError(null);
      })
      .catch((err) => {
        setError(err.message);
      })
      .finally(() => {
        setLoading(false);
      });
  }

  useEffect(() => {
    fetchAll();
    return () => controller.abort();
  }, []);

  return <div></div>;
}

export default App;

Summary

  • A good network request should have a loading and an error state

  • A good network request should be cancelable

  • The loading state tells you if the request is currently running and the error state tells you if the network request was unsuccessful

  • Always cancel all network requests in the cleanup function of the useEffect hook