Get Fetch Results in a React Functional Component

When I first began working with React functional components, one of the most common tasks I faced was fetching data from APIs. Whether it was pulling product data for an eCommerce dashboard or retrieving user information for a client portal, handling fetch results efficiently was crucial.

Over the years, I’ve refined my approach to fetching data in React. In this tutorial, I’ll show you simple, modern, and reliable ways to get fetch results using React hooks like useState and useEffect.

We’ll go through multiple methods, each with complete code examples and concise explanations. By the end of this guide, you’ll know exactly how to fetch data, handle loading and error states, and render results cleanly in your React app.

Method 1 – Fetch Data Using useEffect and useState

The most common way to fetch data in React functional components is by combining useEffect (for side effects) with useState (for managing data).

Here’s a simple example where I fetch random user data from the Random User API.

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

function RandomUser() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch("https://randomuser.me/api/")
      .then((response) => {
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        return response.json();
      })
      .then((data) => {
        setUser(data.results[0]);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, []);

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

  return (
    <div>
      <h2>User Information</h2>
      <p>
        Name: {user.name.first} {user.name.last}
      </p>
      <p>Email: {user.email}</p>
      <img src={user.picture.large} alt="User" />
    </div>
  );
}

export default RandomUser;

You can refer to the screenshot below to see the output.

Get Fetch Results in a React Functional Component

In this method, I use useEffect to trigger the fetch operation when the component mounts. The useState hooks manage the user data, loading status, and error state. Once the data is fetched, the component re-renders to display the result.

Method 2 – Fetch Data Using async/await Inside useEffect

While the .then() syntax works fine, I personally prefer using async/await because it makes the code cleaner and easier to read.

Here’s how you can rewrite the same example using async/await:

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

function RandomUserAsync() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await fetch("https://randomuser.me/api/");
        if (!response.ok) {
          throw new Error("Failed to fetch user data");
        }
        const data = await response.json();
        setUser(data.results[0]);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, []);

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

  return (
    <div>
      <h2>Random User (Async/Await)</h2>
      <p>
        Name: {user.name.first} {user.name.last}
      </p>
      <p>Email: {user.email}</p>
      <img src={user.picture.large} alt="User" />
    </div>
  );
}

export default RandomUserAsync;

You can refer to the screenshot below to see the output.

React Functional Component Get Fetch Results

The async/await syntax makes the asynchronous code look synchronous, improving readability. Using try…catch…finally ensures proper error handling and cleanup once the data is fetched.

Method 3 – Fetch Data When Dependencies Change

Sometimes, you need to refetch data when a dependency changes, for example, when a user selects a different city to view weather data.

Here’s how I handle that scenario:

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

function WeatherData() {
  const [city, setCity] = useState("New York");
  const [weather, setWeather] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchWeather = async () => {
      setLoading(true);
      const response = await fetch(
        `https://api.open-meteo.com/v1/forecast?latitude=40.71&longitude=-74.01&current_weather=true`
      );
      const data = await response.json();
      setWeather(data.current_weather);
      setLoading(false);
    };

    fetchWeather();
  }, [city]);

  return (
    <div>
      <h2>Weather Data for {city}</h2>
      <select onChange={(e) => setCity(e.target.value)}>
        <option>New York</option>
        <option>Los Angeles</option>
        <option>Chicago</option>
      </select>

      {loading && <p>Loading...</p>}
      {weather && (
        <div>
          <p>Temperature: {weather.temperature}°C</p>
          <p>Wind Speed: {weather.windspeed} km/h</p>
        </div>
      )}
    </div>
  );
}

export default WeatherData;

You can refer to the screenshot below to see the output.

Get Fetch Results in React Functional Component

By adding the city as a dependency in useEffect, React automatically refetches data whenever the city value changes. This approach is perfect for dynamic dashboards or search-based applications.

Method 4 – Use a Custom Hook for Fetching Data

As your React app grows, you’ll likely need to fetch data in multiple components. Instead of repeating the same logic everywhere, I prefer creating a custom hook for reusability.

Here’s an example of a reusable useFetch hook:

import { useState, useEffect } from "react";

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

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error("Failed to fetch data");
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

Now you can use this hook in any component:

import React from "react";
import useFetch from "./useFetch";

function UsersList() {
  const { data, loading, error } = useFetch("https://jsonplaceholder.typicode.com/users");

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

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

export default UsersList;

This method keeps your components clean and focused. The useFetch hook encapsulates all the fetching logic, making it reusable for any API endpoint.

Bonus Tip – Handle Abort with Cleanup

When you fetch data in React, it’s good practice to abort ongoing requests if the component unmounts before the fetch completes.

Here’s how you can do that:

useEffect(() => {
  const controller = new AbortController();

  const fetchData = async () => {
    try {
      const response = await fetch(url, { signal: controller.signal });
      const result = await response.json();
      setData(result);
    } catch (err) {
      if (err.name !== "AbortError") setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  fetchData();

  return () => controller.abort();
}, [url]);

Using AbortController helps prevent memory leaks and ensures your app doesn’t try to update state after a component is unmounted.

Best Practices When Fetching Data in React

From my experience, here are a few golden rules to follow:

  1. Always handle loading and error states.
  2. Use async/await for cleaner asynchronous code.
  3. Create reusable hooks for consistency.
  4. Add cleanup functions to avoid memory leaks.
  5. Keep API URLs and keys in environment variables.

When I started building data-driven React apps, fetching data felt repetitive and error-prone. But once I began structuring my fetch logic into hooks and following best practices, my code became cleaner, faster, and easier to debug.

Fetching data is one of the most common tasks in React development, and mastering it early will save you countless hours down the road.

You can also read:

Leave a Comment

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.