🌐 React API Integration & Data Fetching
Estimated reading: 4 minutes 24 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 :

Leave a Reply

Your email address will not be published. Required fields are marked *

Share

πŸ” React API Integration using useEffect

Or Copy Link

CONTENTS
Scroll to Top