Introduction
In almost every real-world React application, you need to communicate with a backend server. Whether you are fetching user data, submitting a form, or loading products from a database, API calls are at the core of how modern web applications work.
But handling API calls properly is not just about fetching data—it’s about managing loading states, handling errors, optimizing performance, and writing clean, maintainable code.
In this article, we will explore how to handle API calls in React using both Fetch API and Axios, understand when to use each, and see how this works in real-world applications.
Understanding API Calls in React
At a basic level, an API call is a request sent from your frontend (React app) to a backend server, and the server responds with data.
For example:
Fetch user details
Submit login form
Load product list
In React, API calls are usually handled inside lifecycle methods or hooks like useEffect, because they involve asynchronous operations.
Using Fetch API in React
Fetch API is a built-in browser feature, so you don’t need to install anything.
Let’s start with a simple example.
import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => {
if (!response.ok) {
throw new Error("Failed to fetch data");
}
return response.json();
})
.then((data) => {
setUsers(data);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default Users;
What’s happening here:
Using Async/Await with Fetch
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error("Failed to fetch data");
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
This version is easier to read and maintain, especially in larger applications.
Using Axios in React
Axios is a popular third-party library that simplifies API calls.
First, install Axios:
npm install axios
Now let’s see the same example using Axios.
import axios from "axios";
import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
setUsers(response.data);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default Users;
Axios with Async/Await
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get("https://jsonplaceholder.typicode.com/users");
setUsers(response.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
Fetch vs Axios
| Feature | Fetch API | Axios |
|---|
| Installation | Not required | Required |
| JSON Handling | Manual (response.json()) | Automatic |
| Error Handling | Manual | Easier |
| Request Interceptors | Not available | Available |
| Timeout Support | No built-in | Yes |
| Browser Support | Native | Requires package |
Real-World Example: Form Submission
const handleSubmit = async () => {
try {
const response = await axios.post("/api/login", {
email: "[email protected]",
password: "123456",
});
console.log(response.data);
} catch (error) {
console.error(error);
}
};
This is commonly used in:
Login systems
Registration forms
Payment processing
Handling Loading, Error, and Empty States
A well-designed React app always handles these states:
Loading → Show spinner or message
Error → Show user-friendly message
Empty → Show “No data available” message
This improves user experience significantly.
When to Use Fetch vs Axios
Use Fetch when:
Use Axios when:
Common Mistakes Developers Make
Not handling errors properly
Not using loading state
Calling API multiple times unnecessarily
Writing API logic inside components (instead of services)
Best Practice: Create API Service Layer
Instead of calling APIs directly in components, create a separate file:
import axios from "axios";
const api = axios.create({
baseURL: "https://api.example.com",
});
export const getUsers = () => api.get("/users");
Then use it in components.
This improves:
Code readability
Reusability
Maintainability
Real-World Use Cases
E-commerce: Fetch product list
Dashboard: Load analytics data
Social app: Fetch posts and comments
Conclusion
Handling API calls in React is a fundamental skill for building modern applications. Whether you use Fetch API or Axios, the goal is to manage data efficiently, handle errors gracefully, and provide a smooth user experience.
Axios is generally preferred in larger applications due to its simplicity and powerful features, while Fetch works well for smaller or lightweight projects.
The key is not just making API calls, but structuring them properly so your application remains scalable, maintainable, and performant.