Fetching Data from an API using useEffect

1 min read

Fetching Data from an API using useEffect

Let’s build a simple React app that fetches and displays user data from an API when the component mounts to learn the useEffect hook in ReactJS.

1. Setting Up the Component

import Reat, { useState, useEffect } from "react";

const UserList = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Fetch data when the component mounts
    const fetchUsers = async () => {
      try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users");
        const data = await response.json();
        setUsers(data); // Update state with user data
      } catch (error) {
        console.error("Error fetching users:", error);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []); // Empty dependency array -> runs only once on mount

  return (
    <div>
      <h2>User List</h2>
      {loading ? <p>Loading...</p> : 
        <ul>
          {users.map(user => (
            <li key={user.id}>{user.name} - {user.email}</li>
          ))}
        </ul>
      }
    </div>
  );
};

export default UserList;


How useEffect Works in This Example

  1. Runs When Component Mounts:
    • The useEffect hook executes the fetchUsers function once when the component is first rendered.
    • The empty dependency array [] ensures that it runs only on the initial render.
  2. Fetches Data Asynchronously:
    • fetchUsers uses async/await to call the API and update the state with user data.
  3. Handles Loading State:
    • The loading state ensures that a loading message is displayed while fetching data.

2. Using useEffect with Dependencies

We can also make useEffect respond to state changes. Let’s create an example where we fetch data when a user selects a different category.

import React, { useState, useEffect } from "react";

const CategoryFetcher = () => {
  const [category, setCategory] = useState("posts");
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/${category}`);
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    fetchData();
  }, [category]); // Runs whenever 'category' changes

  return (
    <div>
      <h2>Data for: {category}</h2>
      <button onClick={() => setCategory("posts")}>Posts</button>
      <button onClick={() => setCategory("users")}>Users</button>
      <button onClick={() => setCategory("comments")}>Comments</button>

      <ul>
        {data.slice(0, 5).map(item => (
          <li key={item.id || item.email}>{JSON.stringify(item)}</li>
        ))}
      </ul>
    </div>
  );
};

export default CategoryFetcher;

Key Takeaways

  • The effect re-runs whenever category changes ([category] in dependencies).
  • Clicking a button updates the category state, triggering a new API call.

3. Cleaning Up Effects (Example: Event Listeners)

If your effect involves event listeners, intervals, or subscriptions, cleanup is necessary to avoid memory leaks.

import React, { useState, useEffect } from "react";

const WindowResize = () => {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize); // Cleanup when unmounting
    };
  }, []); // Runs only on mount and unmount

  return <h2>Window Width: {width}px</h2>;
};

export default WindowResize;

Why Use Cleanup?

  • Without cleanup, multiple event listeners would be created, leading to performance issues.
  • The cleanup function (inside return) runs when the component unmounts.

Summary

Use CaseExample
Fetch API Data on MountuseEffect(() => {...}, [])
Fetch Data on State ChangeuseEffect(() => {...}, [dependency])
Event ListenersuseEffect(() => { addEventListener(); return () => removeEventListener(); }, [])
Cleanup on UnmountReturn a function inside useEffect

Best Practices

  • Use an empty dependency array ([]) if you only want the effect to run once.
  • Always cleanup event listeners, intervals, and subscriptions.
  • Avoid unnecessary API calls by properly managing dependencies.

🤞 Never miss a story from us, get weekly updates to your inbox!