Introduction
Working with APIs is a core part of building real-world React applications. Whether you are fetching user data, product lists, or dashboard stats, it's important to handle API calls correctly. Poorly managed API logic can cause bugs like repeated API calls, missing loading spinners, or inconsistent UI. In this article, you will learn how to make API calls in React using clean, simple code — and how to manage loading, success, and error states using best practices.
Why Handling API Calls Correctly Matters
Incorrect API handling can lead to:
UI flickering or blank screens
Multiple API calls due to re-renders
Errors not showing to the user
Loading states not updating correctly
Memory leaks when using async functions inside useEffect
With proper structure, you can keep your React UI smooth, predictable, and error-free.
Basic API Call in React Using useEffect
The most common way to fetch data in React is using useEffect and fetch or axios.
Example
import { useEffect, useState } from "react";
function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch("https://api.example.com/products")
.then(response => response.json())
.then(result => {
setData(result);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, []);
return <div>...</div>;
}
What This Code Does
loadingbegins astrueAPI request runs once (because dependency array is empty)
If successful → stores data and hides loading
If error → shows error message and hides loading
This structure ensures a clean and predictable UI.
Using Async/Await for Cleaner API Code
Using .then() works fine but async/await is easier to read.
Example
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch("https://api.example.com/users");
const json = await response.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
Why This Is Better
Code looks cleaner
try/catch/finallymakes error handling clearAll states are handled in one place
Showing a Proper Loading Spinner
Users should not see a blank page while data loads.
Example UI
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
Why This Helps
Prevents confusion
Improves user experience
Ensures UI displays only when data is ready

Join the conversation! Your thoughts help the community grow.