π React API Integration using useEffect
β Fetch Data Like a Pro (2025 Guide)
π§² Introduction β Why Use useEffect
for API Calls?
In React.js, useEffect
is the go-to hook for running side effects like API calls. It helps ensure that data fetching:
- π§± Happens after the component renders
- π Re-runs only when dependencies change
- π§Ή Cleans up properly on unmount
Using useEffect
for API integration keeps your code predictable, maintainable, and reactive to UI changes.
π― In this guide, youβll learn:
- How to fetch data using
useEffect
- Manage loading, error, and success states
- Use async/await patterns
- Clean up API calls with
AbortController
βοΈ 1. Basic GET Request with useEffect
import { useEffect, useState } from 'react';
function Posts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((res) => {
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
})
.then((data) => setPosts(data))
.catch((err) => setError(err))
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loading posts...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{posts.map((post) => <li key={post.id}>{post.title}</li>)}
</ul>
);
}
β
Fetches posts on first render
β
Handles loading and error states
π 2. Using async/await
Inside useEffect
Reactβs useEffect
doesnβt support async directly, but you can wrap it in an Immediately Invoked Function Expression (IIFE).
useEffect(() => {
(async () => {
try {
const res = await fetch('/api/data');
if (!res.ok) throw new Error('Server Error');
const json = await res.json();
setData(json);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
})();
}, []);
π Use try/catch/finally
for safe async handling
π§Ή 3. Cleanup API Calls with AbortController
Cancel fetch calls if the component unmounts before the request completes.
useEffect(() => {
const controller = new AbortController();
fetch('/api/items', { signal: controller.signal })
.then(res => res.json())
.then(setItems)
.catch(err => {
if (err.name !== 'AbortError') setError(err);
});
return () => controller.abort(); // Cleanup
}, []);
β
Prevents memory leaks and invalid state updates
β
Important in slow or paginated requests
π¦ 4. Trigger API Call on Dependency Change
useEffect(() => {
if (!userId) return;
setLoading(true);
fetch(`/api/user/${userId}`)
.then(res => res.json())
.then(setUser)
.catch(setError)
.finally(() => setLoading(false));
}, [userId]);
β
Re-fetches user data only when userId
changes
π§ 5. Refactor into a Custom Hook
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((res) => res.json())
.then(setData)
.catch((err) => {
if (err.name !== 'AbortError') setError(err);
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
β Usage:
const { data, loading, error } = useFetch('/api/products');
π Improves reusability across components
π Best Practices
β
Use useEffect
only for side effects
β
Manage loading/error states to improve UX
β
Cleanup long requests with AbortController
β
Avoid fetching on every renderβuse dependencies wisely
β
Abstract fetch logic into reusable hooks for scaling
π Summary β Recap & Next Steps
Reactβs useEffect
is perfect for integrating with APIs. With correct patterns for async/await
, cleanup logic, and dependency control, you can build reliable and responsive UIs backed by real-time data.
π Key Takeaways:
- Use
useEffect
to call APIs on mount or dependency change - Wrap async logic in an IIFE for cleaner code
- Handle loading and error states gracefully
- Cancel requests with
AbortController
to prevent leaks - Extract logic into reusable
useFetch
hooks
βοΈ Real-World Relevance:
Used for product listings, user profiles, dashboards, and real-time data in apps like Airbnb, Notion, and LinkedIn.
β FAQ Section
β Can useEffect
be async directly?
β No. Wrap it in an IIFE or call an async function from inside it.
β Why use AbortController
in fetch?
β
To cancel the request when a component unmounts or before it re-runs.
β How do I prevent unnecessary API calls in useEffect
?
β
Add proper dependency arrays and conditional checks inside the effect.
β Should I use Axios inside useEffect
?
β
Yes. Just like fetch, you can call Axios inside useEffect
:
axios.get('/api/data').then(res => setData(res.data));
β Can I use useEffect
for POST requests too?
β
Yes, though POSTs are usually triggered by user actions (like form submissions), not initial renders.
Share Now :