React useEffect — Side Effects and Data Fetching (React 19+)

react useeffect tutorial — React useEffect — Side Effects and Data Fetching (React 19+)

Written by

in

Updated July 4, 2026. Refreshed for React 19 and current SEO best practices.

React 19 Notes

Use AbortController to cancel in-flight fetches when components unmount. For data-heavy apps consider TanStack Query; for this series, useEffect + fetch teaches the fundamentals clearly.

This lesson explains React useEffect — the hook for side effects that run after render: fetching data, timers, DOM subscriptions, and syncing with external systems. You will fetch JSON from a public API and clean up resources correctly.

Prerequisites: Lessons 1–4, especially useState. Estimated time: 50–60 minutes.

1. What Is a Side Effect?

A side effect is work that reaches outside the component’s render output: network requests, localStorage, document.title, WebSocket listeners, or setInterval. React expects render to be pure — effects belong in useEffect.

Fetching API data in a React application
Photo by Luke Chesser on Unsplash

2. useEffect Syntax

import { useEffect, useState } from 'react';

function PageTitle({ title }) {
  useEffect(() => {
    document.title = title;
  }, [title]);

  return <h1>{title}</h1>;
}

The second argument is the dependency array. React re-runs the effect when any dependency changes. An empty array [] runs once after mount (like componentDidMount).

JavaScript developer working with async data
Photo by Florian Olivo on Unsplash

3. Fetch Data on Mount

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

  useEffect(() => {
    async function loadUsers() {
      try {
        const res = await fetch('https://jsonplaceholder.typicode.com/users');
        if (!res.ok) throw new Error('Failed to fetch');
        const data = await res.json();
        setUsers(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    loadUsers();
  }, []);

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

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

This pattern — loading, error, and data state — is standard in production React apps and frameworks like Next.js.

4. Cleanup Functions

Return a function from useEffect to clean up before the effect runs again or when the component unmounts:

useEffect(() => {
  const id = setInterval(() => console.log('tick'), 1000);
  return () => clearInterval(id);
}, []);

Always clean up subscriptions, timers, and event listeners to prevent memory leaks — a core topic in any react useeffect tutorial.

5. Dependency Array Pitfalls

  • Missing deps — stale closures; ESLint react-hooks/exhaustive-deps warns you
  • Object/array deps — new reference every render causes infinite loops; memoize or lift state
  • No array — effect runs after every render (rarely what you want)

6. Abort Fetch on Unmount (React 19)

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

  fetch(url, { signal: controller.signal })
    .then((res) => res.json())
    .then(setData)
    .catch((err) => {
      if (err.name !== 'AbortError') setError(err.message);
    });

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

Aborting prevents setting state on an unmounted component — especially important in React Strict Mode where effects run twice in development.

7. Next Steps

Effects connect React to the outside world. Next you will build controlled forms with state and validation patterns used in real apps.

Series: Lesson 4 · Lesson 6 — Forms

Frequently Asked Questions

When should I use useEffect?
Use it when you need to synchronize with something outside React’s render — APIs, browser APIs, third-party widgets, or timers.

Can I fetch data without useEffect?
Yes. React 19 and frameworks like Next.js support fetching in Server Components or with libraries like TanStack Query. useEffect fetch is still the standard pattern in client-only Vite SPAs.

Why does my useEffect run twice in development?
React Strict Mode intentionally double-invokes effects in development to surface cleanup bugs. Production behavior runs once per dependency change.


Want live React or frontend classes? Join Alkademy for instructor-led React and JavaScript courses with hands-on projects.

Comments

Leave a Reply

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