React useEffect Guide: When to Use It and When to Avoid It
Web

React useEffect Guide: When to Use It and When to Avoid It


Almost everyone who studies React eventually runs into useEffect, and many people start using it too early and too often. It is common to think, “something changed, so I probably need an effect,” and that is how loops, awkward dependencies, and confusing control flow begin.

useEffect is powerful, but it is also one of the easiest ways to make React code harder to understand. That is why it helps to learn the purpose before the syntax.

This post covers three things.

  • when useEffect is actually the right tool
  • how to think about dependency arrays
  • which problems are simpler without an effect at all

The key idea is this: useEffect is for synchronizing with the outside world after rendering.

What React useEffect is for

useEffect lets you run logic after React renders. It is commonly used for cases like these.

  • fetching data from an API
  • adding and removing event listeners
  • starting and cleaning up timers
  • syncing with browser APIs
  • coordinating React state with external libraries
useEffect(() => {
  async function fetchPosts() {
    const response = await fetch('/api/posts');
    const data = await response.json();
    setPosts(data);
  }

  fetchPosts();
}, []);

An empty dependency array means the effect is designed to run on mount.

When you really need useEffect

A simple test is to ask whether rendering alone can solve the problem. If the answer is no because the logic reaches outside React, an effect is often appropriate.

Good examples include:

  • subscribing to browser events
  • starting intervals or timeouts that need cleanup
  • sending network requests after a component appears
  • syncing document title or another browser-managed value
useEffect(() => {
  function handleScroll() {
    console.log(window.scrollY);
  }

  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, []);

This is a classic effect because registration and cleanup both matter.

When you should avoid useEffect

Many things that beginners place inside effects are really just calculations or event logic.

You often do not need an effect for:

  • derived values from existing props or state
  • click-based logic that can run directly in a handler
  • simple form updates
  • values that can be calculated during render

For example, this does not need an effect.

const totalPrice = items.reduce((sum, item) => sum + item.price, 0);

If you create separate state like totalPrice and keep it in sync through useEffect, you usually add complexity without adding value.

How to think about dependency arrays

The dependency array declares which values an effect should react to. If the effect uses values that can change between renders, those values usually belong in the array.

useEffect(() => {
  document.title = `Count: ${count}`;
}, [count]);

Here, the browser title should update whenever count changes, so [count] makes sense.

A common trap is forcing the array to stay empty to silence warnings. If a warning appears, it is usually better to ask whether the effect belongs somewhere else or whether the logic should be written differently.

Common mistakes

1. Creating derived state inside an effect

Values like filteredItems, fullName, or isFormValid are often simpler as direct calculations.

2. Moving event-driven logic into effects

If a button click should trigger work, a click handler is usually clearer than an effect that reacts later to changed state.

3. Forgetting cleanup

Intervals, subscriptions, and event listeners need cleanup. Otherwise duplicated behavior and memory leaks become more likely.

4. Repeating fetch boilerplate everywhere

On small demos, direct fetch logic inside useEffect is fine. In bigger apps, loading state, error state, retries, and caching often repeat enough that a custom hook becomes worthwhile.

Questions to ask before writing an effect

  1. Am I synchronizing with something outside React?
  2. Could this be a direct calculation during render?
  3. Could this run in an event handler instead?
  4. Does this need cleanup?
  5. Is this pattern repeating enough to extract into a hook?

Just asking these questions removes a lot of unnecessary effects.

FAQ

Q. Does all data fetching have to happen in useEffect?

Not always. In many setups it is common, but frameworks and data libraries may offer better patterns.

Q. Should I ignore dependency warnings if the code seems to work?

Usually no. Those warnings often point to unstable behavior that may fail later.

Q. Is having many effects automatically a code smell?

Not automatically, but more effects usually mean more synchronization points, which is worth reviewing.

Start Here

Continue with the core guides that pull steady search traffic.