Web API  

How to Fetch and Display API Data in React Using useEffect and useState

Introduction

In modern web development, most applications depend on external data sources such as APIs. Whether you're building a dashboard, blog, e-commerce site, or admin panel, fetching data from an API is a core skill every React developer must understand.

React provides powerful tools like useState and useEffect hooks that make it easy to fetch and display API data in a clean and efficient way. In this article, we will learn step-by-step how to fetch API data in React, store it in state, and display it on the UI.

This guide is written in simple and practical language, making it easy for beginners to understand while still being useful for experienced developers.

What is an API?

An API (Application Programming Interface) allows different software systems to communicate with each other. In simple terms, an API lets your React application request data from a server and receive it in a structured format, usually JSON.

For example:

  • Fetching user data from a server

  • Getting product details for an e-commerce app

  • Loading blog posts dynamically

Understanding useState in React

The useState hook is used to store and manage data inside a React component.

When working with APIs, we use useState to:

  • Store fetched data

  • Track loading state

  • Handle errors

Example:

const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

Explanation:

  • data → stores API response

  • loading → tracks whether data is being fetched

  • error → stores any error that occurs

Understanding useEffect in React

The useEffect hook is used to perform side effects in React components. Fetching data from an API is considered a side effect.

useEffect runs when the component is rendered.

Example:

useEffect(() => {
  // API call here
}, []);

The empty dependency array [] ensures the API is called only once when the component loads.

Step-by-Step: Fetch API Data in React

Let’s build a simple example where we fetch user data from a public API and display it.

Step 1: Create a React Component

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

function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  return <div></div>;
}

export default Users;

Step 2: Fetch Data Using useEffect

useEffect(() => {
  fetch("https://jsonplaceholder.typicode.com/users")
    .then((response) => {
      if (!response.ok) {
        throw new Error("Failed to fetch data");
      }
      return response.json();
    })
    .then((data) => {
      setUsers(data);
      setLoading(false);
    })
    .catch((error) => {
      setError(error.message);
      setLoading(false);
    });
}, []);

Explanation:

  • fetch() → makes API request

  • response.json() → converts response to JSON

  • setUsers() → saves data

  • setLoading(false) → stops loading state

Step 3: Display the Data in UI

if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;

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

Explanation:

  • Conditional rendering handles loading and errors

  • map() is used to display list data

Complete Example Code

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

function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then((response) => {
        if (!response.ok) {
          throw new Error("Failed to fetch data");
        }
        return response.json();
      })
      .then((data) => {
        setUsers(data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

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

export default Users;

Using Async/Await (Recommended Approach)

Using async/await makes the code cleaner and easier to read.

useEffect(() => {
  const fetchData = async () => {
    try {
      const response = await fetch("https://jsonplaceholder.typicode.com/users");
      if (!response.ok) {
        throw new Error("Failed to fetch data");
      }
      const data = await response.json();
      setUsers(data);
    } catch (error) {
      setError(error.message);
    } finally {
      setLoading(false);
    }
  };

  fetchData();
}, []);

Best Practices for Fetching API Data in React

  1. Always handle loading state

    Users should know when data is being loaded to improve user experience.

  2. Handle errors properly

    Display meaningful error messages instead of crashing the app.

  3. Use async/await for better readability

    It makes your code easier to maintain and debug.

  4. Avoid unnecessary re-renders

    Use dependency arrays correctly in useEffect.

  5. Separate API logic

    Keep API calls in a separate file for cleaner code in large projects.

Common Mistakes to Avoid

  • Forgetting dependency array in useEffect

  • Not handling errors

  • Updating state after component unmount

  • Calling API multiple times unnecessarily

Real-World Use Cases

  • Dashboard applications fetching analytics data

  • E-commerce websites loading products

  • Social media apps displaying posts

  • Admin panels showing user details

Conclusion

Fetching and displaying API data in React using useState and useEffect is one of the most important skills for modern frontend development.

By understanding how to manage state, handle side effects, and display dynamic data, you can build powerful and scalable React applications.

Start with simple examples like this, then move to advanced concepts like custom hooks, caching, and state management libraries.