Hey there! Welcome to Hostman! 🎉

Using React with Axios

06.12.2023
Reading time: 19 min
Hostman Team
Technical writer

Axios is a JS library that provides an easy-to-use interface for executing HTTP requests in the browser and on the server side. It supports the Promise API and allows you to handle asynchronous requests. 

The library is widely used in various projects, including React, Vue.js, Angular, and other frameworks. It lets developers easily interact with the API and process data received from the server.

There are numerous benefits of using this library in React:

Ease of use: It has a simple and easy-to-understand interface, making it easy to use even for beginners. It allows developers to quickly send HTTP requests to the server and receive responses from it.

Versatility: Supports browsers and Node.js, making it ideal for client-side and server-side use in React projects.

Promises support: Returns a Promises object, making it ideal for handling modern JavaScript syntax and asynchronous operations.

Interceptors: Allows you to use interceptors so that requests and responses are processed before they are sent and after they are received. This is useful for adding headers, handling errors, and more.

Request cancellation: The library allows you to cancel requests, improving application performance and preventing unnecessary requests.

Support for request headers and parameters: Allows you to add request headers and parameters, which is useful when passing information to the server.

Broad functionality: Supports all major HTTP request methods: GET, POST, PUT, DELETE, etc., and many data types such as JSON, forms, and text.

Error handling: It has built-in error handling, which allows you to detect and handle errors that occur during the execution of requests.

The requirements for using Axios are: 

  • Node.js version 14 and later;

  • A project on React set up with create-react-app tool;

  • Basic knowledge of JS, HTML, and CSS.

In this article, we will learn how to use Axios with React and give examples of its application.

Installing Axios and importing it into a project

Step 1: Before you start working with Axios, you need to select a working project:

cd project_name

Or create a new one and then select it:

npx create-react-app project_name
cd project_name

We'll create a React project called hostman

Step 2: Next, you need to install the library using the npm package manager. To do this, run the following command in the terminal:

npm install axios

Step 3. After the download is complete, import the library into the component that requires its use:

import axios from 'axios';

Once the Axios library has been installed and imported into your React application, we can begin our work.

GET request

To send a GET request, use the axios.get() method, which accepts one parameter, the URL. You should not use it to send sensitive data, as it may be visible in the browser address bar and server logs.

Step 1. Move from the application directory to the src directory.

cd src

Step 2. Next, create a file with the .js extension:

nano Test1.js

Step 3. Now, let's move on to writing the code:

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

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

  useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then((response) => {
        setData(response.data);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  return (
    <div>
      <h1>List of users:</h1>
      <ul>
        {data.map((user) => (
          <li key={user.id}>
            {user.name} ({user.email})
          </li>
        ))}
      </ul>
    </div>
  );
}

export default Test1;

In this example, Test1 sends a GET request to the specified service and displays the list of users on the page. The component uses two hooks: 

  • useEffect to execute the request; 

  • useState to store data received from the API. 

A hook is a function that allows you to use state and other React features in functional components. If the request failed to send or process, we output an error to the console.

As a result of the request, we'll receive a list of users.

This example and all others use the https://jsonplaceholder.typicode.com API, which provides fake data for testing purposes. Replace it with the URL you will use in your project.

POST request

POST requests send data to the server, such as forms with user data, images, or other files, or creating or updating records in a database.

POST requests can be safe if they don't change the server state, but they are not idempotent, which means that sending a duplicate request again may change the server state. To make them idempotent, you can set a special header, such as an Idempotency-Key, which allows the server to determine if the request has been sent before. 

You can also protect POST requests with CSRF tokens to prevent them from being forged by attackers.

To send a POST request, call the post() method and pass the data and URL to which you want to send it. 

Step 1: Open the previously created file for editing:

nano Test1.js

Step 2. Write new code:

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

function Test1() {
  const [data, setData] = useState({ name: "", email: "" });
  const [response, setResponse] = useState("");

  const handleChange = (event) => {
    setData({ ...data, [event.target.name]: event.target.value });
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    axios
      .post("https://jsonplaceholder.typicode.com/users", data)
      .then((response) => {
        setResponse(response.data);
      })
      .catch((error) => {
        console.log(error);
      });
  };

  return (
    <div>
      <h1>Sending data to server</h1>
      <form onSubmit={handleSubmit}>
        <label>
          Name:
          <input
            type="text"
            name="name"
            value={data.name}
            onChange={handleChange}
          />
        </label>
        <br />
        <label>
          Email:
          <input
            type="email"
            name="email"
            value={data.email}
            onChange={handleChange}
          />
        </label>
        <br />
        <button type="submit">Send</button>
      </form>
      {response && (
        <p>
          Data sent successfully: {response.name} ({response.email})
        </p>
      )}
    </div>
  );
}

export default Test1;

In this example, we applied useState to store the data entered by the user in the form and the data received from the API. We also created two functions: 

  • handleChange to update the state of the form when the user changes the data;

  • handleSubmit to send a POST request when the form is submitted.

When the form is submitted, we send a POST request and process the received data using the setResponse method. If the request cannot be sent or processed, we output an error.

PUT request

PUT requests send data to a server, which will replace existing data. They are often used to update user information, web page content, or database data.

To send a PUT request, you need to call the put() method and pass the data and the URL of the resource to which you want to send it. 

Step 1: Open the component file for editing:

nano Test1.js

Step 2. Let's rewrite the code of the Test1.js file again:

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

const Test1 = () => {
  const [postData, setPostData] = useState({ id: "", title: "", body: "" });
  const [putData, setPutData] = useState({ id: "", title: "", body: "" });

  const handleInputChange = (event) => {
    setPostData({ ...postData, [event.target.name]: event.target.value });
};

  const handleSubmit = (event) => {
    event.preventDefault();
    axios
      .put(`https://jsonplaceholder.typicode.com/posts/${postData.id}`, {
        title: postData.title,
        body: postData.body,
      })
      .then((response) => {
        setPutData(response.data);
        console.log(response);
      })
      .catch((error) => {
        console.log(error);
      });
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="id">ID: </label>
          <input
            type="text"
            name="id"
            value={postData.id}
            onChange={handleInputChange}
          />
        </div>
        <div>
          <label htmlFor="title">Title: </label>
          <input
            type="text"
            name="title"
            value={postData.title}
            onChange={handleInputChange}
          />
        </div>
        <div>
          <label htmlFor="body">Info: </label>
          <input
            type="text"
            name="body"
            value={postData.body}
            onChange={handleInputChange}
          />
        </div>
        <button type="submit">Update</button>
      </form>
      <div>
        <p>Response:</p>
        <p>ID: {putData.id}</p>
        <p>Title: {putData.title}</p>
        <p>Info: {putData.body}</p>
      </div>
    </div>
  );
};

export default Test1;

Here, useState controls two states: 

  • postData that contains the data we want to send with the PUT request; 

  • putData that contains data received in response to the request.

We also define two functions: 

  • handleInputChange that updates the postData state when the user adds data to the form;

  • handleSubmit that sends a PUT request and updates the putData state.

Finally, we display the received data in the putData after the successful execution of the request.

DELETE request

To send a DELETE request, you need to call the delete() method and pass it the URL of the resource you want to delete. After receiving the request, the server checks if the resource is on the server and, if it is, deletes it. If the resource is not on the server, it may return a 404 "Not Found" error.

Step 1: Open the component file for editing:

nano Test1.js

Step 2. Rewrite the Test1.js code again:

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

const Test1 = () => {
  const [postId, setPostId] = useState(null);
  const [response, setResponse] = useState(null);

  const handleDelete = () => {
    axios.delete(`https://jsonplaceholder.typicode.com/posts/${postId}`)
      .then(res => setResponse(res))
      .catch(err => setResponse(err));
  }

  return (
    <div>
      <input type="number" placeholder="Enter Post ID" onChange={(e) => setPostId(e.target.value)} />
      <button onClick={handleDelete}>Delete Post</button>
      {response && (
        <div>
          <h2>Response</h2>
          <p>Status: {response.status}</p>
          <p>Data: {JSON.stringify(response.data)}</p>
        </div>
      )}
    </div>
  );
}

export default Test1;

In this component, we created a form where the user can enter the ID of the record they want to delete and submit a DELETE request using the axios.delete() method. We have also handled success and error responses using the then() and catch() methods respectively.

Let's check the result of the component. It is shown in the image below.

Fd22b479 6fbb 4b8f 8c62 8c3a173358d2

Error handling

Axios provides a convenient way to handle errors that occur when sending HTTP requests. The then() and catch() methods accept functions that will be executed depending on the request execution status (whether it was completed or an error occurred). 

Step 1: Open the component file for editing:

nano Test1.js

Step 2. Once again, rewrite the code of the Test1.js file:

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

const Test1 = () => {
  const [error, setError] = useState(null);

  const handleButtonClick = () => {
    axios
      .get("https://jsonplaceholder.typicode.com/posts/invalid-url")
      .then((response) => {
        console.log(response);
      })
      .catch((error) => {
        setError(error);
      });
  };

  return (
    <div>
      <button onClick={handleButtonClick}>Send GET-request</button>
      {error && (
        <div>
          <h2>Error message:</h2>
          <p>{error.message}</p>
        </div>
      )}
    </div>
  );
};

export default Test1;

In the example above, we create an error state. It allows us to keep track of errors that occur when submitting requests. We then create a handleButtonClick function that sends a GET request to an invalid URL and, if an error occurs, uses the setError method to update the error state with an error message.

The return value of the component contains a button and a data block. The button calls the handleButtonClick function, and the data block is only displayed if the error is non-null or false. In the block, we display the error message using the message property of the error object.

Let's check the result. It is shown in the picture below.

9326c475 3059 47cb A97f Cd02e5a26864

In addition, Axios also provides a static isCancel() method that lets you check if an error results from canceling a request. This can be useful if you use CancelToken or AbortController to cancel a request. We'll talk about it further in the article.

Creating a base instance

A base instance is an object that contains configuration parameters and methods for creating requests.

To set up a base instance, you need to use the create() method and pass a settings object. In the settings object, you can specify a base URL for all requests, headers, an authorization token, and other parameters.

For example, if the application needs to send requests to the API with the base URL, you can create a base instance in the src directory as follows.

Step 1: Create a file with the .js extension:

nano basic.js

Step 2. Next, let's write the code:

import axios from 'axios';

const instance = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com/',
});

export default instance;

Step 3. Let's import an instance using the example from the "Error Handling" section:

import React, { useState } from "react";
import axios from "axios";
import instance from "./basic";

const Test1 = () => {
  const [error, setError] = useState(null);

  const handleButtonClick = () => {
  instance
      .get("invalid-url")
      .then((response) => {
        console.log(response);
      })
      .catch((error) => {
        setError(error);
      });
  };

...

export default Test1;

Here we changed the following lines: 

    instance
     .get("invalid-url")

And:

import instance from "./basic";

Using async and await

Async and Await are tools for managing asynchronous operations in JavaScript. They greatly simplify the code involved in handling query responses.

To use these tools, you need first to define a function that will be called asynchronously. Then, you can use the await keyword to call the Axios function and wait for it to fulfill the request.

Let's rewrite the code from the GET request section:

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

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

    useEffect(() => {
        async function asyncFunction() {
            try {
                const response = await axios.get("https://jsonplaceholder.typicode.com/users");
                setData(response.data);
            } catch (error) {
                console.log(error);
            }
        }
        asyncFunction();
    }, []);

return (
<div>
    <h1>List of users:</h1>
    <ul>
        {data.map((user) => (
        <li key={user.id}>
            {user.name} ({user.email})
        </li>
        ))}
    </ul>
</div>
);
}

export default Test1;

Here, we create asyncFunction, an asynchronous function that waits for a response from the server using the await keyword and sets the received data using setData

With these tools, you can avoid using callbacks or then chains which makes the code more readable. It is important to remember that they also require error handling to prevent blocking the execution flow and unexpected program behavior.

Canceling queries

If an application uses Axios to execute queries, you may need to cancel them at runtime. 

Here are some examples of when this may be required:

  • A user clicks on the “Cancel” button during a request to the server because they no longer want to wait for a response or have navigated to another page.

  • An error occurred in the application, and the request is no longer needed.

  • The user has started entering a new request, and we must cancel the previous one to avoid overloading the server with requests.

  • The application makes several successive requests to the server, and each request depends on the previous one. If one of these requests terminates with an error, the following requests may become useless, and we should cancel them to reduce the load on the server.

To cancel requests, use the standard AbortController API, added to browsers in ECMAScript 2017. This API provides a convenient mechanism for canceling any asynchronous operations, including requests to the server.

To use AbortController, create an instance with the same name and pass it as a request parameter using the signal method. 

Let's create an instance and declare its signal:

const controller = new AbortController();
const signal = controller.signal;

Then pass it to the query parameters:

axios.get('/user', { signal })
.then((response) => {
 console.log(response.data);
})

And add a cancel notification feature:

if (axios.isCancel(error)) {
   console.log('Request canceled', error.message);
}

To cancel the request, we need to call the abort() method on the instance. Let's add it at the end:

controller.abort();

Using the AbortController API to cancel requests instead of CancelToken is a simpler and more natural way since it is already the standard API for canceling asynchronous operations in the browser. This can also simplify code support and compatibility with other libraries and frameworks using this method.

Optimizing performance when using Axios

In this chapter, we will look at a few tips to optimize performance when using Axios in your project:

Request caching

Caching is useful when we have to send the same request multiple times within a short period of time. For this, we can use the axios-cache-adapter library or write our caching handler.

Minimizing the number of requests

Sometimes we can get all the data we need from the server in a single request, instead of sending separate requests for different data. When possible, we should combine multiple requests into one and get all the required data from the server.

Canceling requests

As we mentioned earlier, request cancelation can be useful when we send many requests or when the user switches between pages quickly. Un-canceled requests can overload the server and slow down the application's performance.

Deferred data download

In some cases, we can delay loading data until the user needs it. For example, we can load data only when the user scrolls the page down or opens a modal window. This can reduce the number of requests and speed up the application's performance.

Using interceptors

Interceptors allow us to manipulate requests and responses before they are sent or processed. We can use them to log requests and responses, handle errors, etc. However, using too many interceptors can degrade application performance.

Using memoization

You can use memoization libraries such as React.memo or useMemo to avoid re-rendering components that have not changed. This can reduce the number of requests to the server and improve performance.

Conclusion

Axios is a library for sending HTTP requests that can be used with React. In this article, we went over the basics of using Axios and gave some examples of its usage.