New! Explore our Programming Academy and AI Tutor - learn to code from scratch, free to start. Explore Now
Programming› React› Effects and Data Fetching

Effects and Data Fetching

💻 React

Load data with useEffect, handle loading and avoid race conditions.

Lesson 6 of 9 Tutorial
0 of 9 completed

An effect runs after render. It is the right place for fetching data, setting up subscriptions and syncing with something outside React.

By the end of this lesson you can:
  • Fetch data in useEffect.
  • Handle loading and errors.
  • Clean up an effect.

Fetch on Mount

An effect with an empty dependency array runs once after the first render.

import { useEffect, useState } from "react";

function Students() {
  const [students, setStudents] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function load() {
      try {
        const response = await fetch("/api/students");
        setStudents(await response.json());
      } finally {
        setLoading(false);
      }
    }

    load();
  }, []);

  if (loading) return <p>Loading…</p>;
  return <ul>{students.map(s => <li key={s.id}>{s.name}</li>)}</ul>;
}

Avoid Race Conditions

If the component unmounts before the request finishes, state updates are wasted. Use an abort controller to cancel the request and ignore late results.

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

  fetch("/api/students", { signal: controller.signal })
    .then(response => response.json())
    .then(setStudents)
    .catch(error => {
      if (error.name !== "AbortError") setError(error.message);
    });

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

Dependencies

List every value the effect reads. An empty array means run once; omitting the array means run after every render, which is rarely what you want.

Warning Do not fetch in the render body. It runs on every render and can create an infinite loop.

Practice

  1. Fetch a list of posts and render the titles.
  2. Show an error message when the request fails.
  3. Add a refresh button that re-runs the fetch.

Hands-on exercise

Build a Posts component that:
1. Fetches /api/posts on mount.
2. Shows Loading while waiting.
3. Shows an error message on failure.
4. Renders each post title with its id as the key.

Recap

Effects run after render and are the right place for data loading. Clean up requests and list dependencies honestly.

Example

import { useEffect, useState } from "react";

function Students() {
  const [students, setStudents] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function load() {
      try {
        const response = await fetch("/api/students");
        setStudents(await response.json());
      } finally {
        setLoading(false);
      }
    }

    load();
  }, []);

  if (loading) return <p>Loading…</p>;
  return <ul>{students.map(s => <li key={s.id}>{s.name}</li>)}</ul>;
}

Exercises

Exercise 1. Posts loader Medium
Build a Posts component that:
1. Fetches /api/posts on mount.
2. Shows Loading while waiting.
3. Shows an error message on failure.
4. Renders each post title with its id as the key.
React exercise-1.txt
Expected Output
Loading, error and success states all handled.

Lesson Nav

Lesson sections