DEV Community

The Practical Developer
The Practical Developer

Posted on Originally published at practicaldevblog.blogspot.com

React useEffect Explained: When to Use It, and When to Avoid It

Introduction

useEffect is one of the most misunderstood React hooks. Beginners use it for everything. Experienced developers use it only when a component needs to sync with something outside React.

The simple rule is this: if the code is a reaction to a user action, like a button click, use an event handler. If the code needs to run because the component is on screen, like fetching data or starting a timer, useEffect may be the right tool.

What useEffect actually does

useEffect runs after React has rendered your component and updated the screen. It is for side effects — work that affects the outside world, not just the calculation of what to show.

Common valid uses:

  • Fetching data when a component loads
  • Subscribing to a WebSocket or event listener
  • Starting and cleaning up a timer
  • Syncing with browser APIs, like document title or local storage

A correct data-fetching example

import { useEffect, useState } from "react";

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

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

    async function loadUser() {
      setLoading(true);
      try {
        const response = await fetch(
          `https://jsonplaceholder.typicode.com/users/${userId}`,
          { signal: controller.signal }
        );
        const data = await response.json();
        setUser(data);
      } catch (error) {
        if (error.name !== "AbortError") {
          console.error(error);
        }
      } finally {
        setLoading(false);
      }
    }

    loadUser();

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

  if (loading) return <p>Loading...</p>;
  if (!user) return <p>No user found.</p>;
  return <h1>{user.name}</h1>;
}
Enter fullscreen mode Exit fullscreen mode

Three things matter here:

  1. The dependency array [userId] means the effect re-runs only when userId changes.
  2. The cleanup function aborts the request if the component unmounts or userId changes.
  3. State updates happen inside the effect, not during render.

When NOT to use useEffect

Do not use useEffect to:

  • Transform data for display. Compute it directly during render instead.
  • Handle a form submit or button click. Use an event handler.
  • Chain state updates that could be calculated from existing state.

A common mistake is fetching data, storing it in state, then using another effect to derive a second value from it. In most cases, you can derive that second value directly during render, with no effect at all.

Checklist before you write useEffect

  • Is this work triggered by rendering, or by a user action? If it is a user action, use an event handler.
  • Does it need cleanup? If yes, return a cleanup function.
  • What should trigger it again? Put only those values in the dependency array.
  • Can the same result be computed during render? If yes, skip the effect.

Takeaway

useEffect is not a place to put all logic. It is a narrow tool for syncing a component with the outside world. Use it for fetching, subscriptions, and timers, and let rendering and event handlers do the rest. That one distinction will prevent most useEffect bugs.


Originally published at https://practicaldevblog.blogspot.com/2026/10/react-useeffect-explained-when-to-use.html

Top comments (0)