🌐 React API Integration & Data Fetching
Estimated reading: 4 minutes 291 views

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 :
Share

πŸ” React API Integration using useEffect

Or Copy Link

CONTENTS
Scroll to Top