When Should You Actually Use useEffect? 12 Cases to Avoid Overusing Effects

This article explains why useEffect in React is an "escape hatch" between functional and imperative code, outlines common pitfalls, provides twelve concrete scenarios where useEffect should be avoided or replaced, and offers best‑practice alternatives such as inline calculations, useMemo, event handlers, state lifting, and custom hooks.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
When Should You Actually Use useEffect? 12 Cases to Avoid Overusing Effects

useEffect as an Escape Hatch

useEffect is React's "escape hatch" that lets you step from the pure functional world into imperative code. Using it at the right time makes your code elegant and efficient; misuse adds unnecessary overhead.

95% of this article comes from the official documentation and follows recommended patterns.

Common Situation

When a state variable name changes and you want to trigger a request, you might write:

useEffect(() => {
  query();
}, [name]);

Later, if name is changed temporarily and you don't need a request, you can guard it:

useEffect(() => {
  if (isReFresh) {
    query();
  }
}, [name]);

If another dependency like age also requires a request, just add it to the dependency array:

useEffect(() => {
  if (isReFresh) {
    query();
  }
}, [name, age]);

Over time you may notice that whether a request is sent depends on the if condition, the dependencies ( name, age), and many other requirements, making it hard to reason about.

Effect Lifecycle

Unlike component lifecycle (mount, update, unmount), an Effect can only:

Start synchronously some work

Later stop synchronously that work

If an Effect depends on props or state, it may run many times. React also provides linter rules to ensure you list the correct dependencies.

Why Use Effect Less?

Updating data to re‑render: When you update state, React first renders the component, then runs Effects. If an Effect immediately updates state again, the whole process restarts, causing extra renders.

User events: Effects run after the render, so you may not know which user action caused the state change.

The official documentation calls Effects a "escape hatch"—only use them when absolutely necessary.

When You Can Skip useEffect

When the result can be computed directly from props or state during render.

When you need to cache expensive calculations—use useMemo instead.

1. Deriving from Props or State

Bad:

function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');
  const [fullName, setFullName] = useState('');
  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);
}

Good:

function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');
  const fullName = firstName + ' ' + lastName;
}

Computing during render avoids extra renders and keeps the code simpler.

2. Caching Expensive Calculations

If a calculation is cheap, just compute it. If it becomes expensive, wrap it with useMemo:

const visibleTodos = useMemo(() => getFilteredTodos(todos, filter), [todos, filter]);
Note: useMemo does not speed up the first render; it only skips unnecessary work on later renders.

3. Resetting All State When Props Change

Instead of using an Effect to clear state when userId changes, give each component a unique key:

export default function ProfilePage({ userId }) {
  return <Profile userId={userId} key={userId} />;
}
function Profile({ userId }) {
  const [comment, setComment] = useState('');
}

This automatically resets the internal state.

4. Resetting Part of State When Props Change

Rather than an Effect, adjust state directly during render or keep previous values to detect changes:

const [prevItems, setPrevItems] = useState(items);
if (items !== prevItems) {
  setPrevItems(items);
  setSelection(null);
}

5. Logic in Event Handlers Instead of Effects

For actions like showing a notification after a button click, place the logic in the click handler, not in an Effect that runs on every render.

function ProductPage({ product, addToCart }) {
  function buyProduct() {
    addToCart(product);
    showNotification(`Added ${product.name} to the shopping cart!`);
  }
  function handleBuyClick() { buyProduct(); }
  function handleCheckoutClick() { buyProduct(); navigateTo('/checkout'); }
}

6. POST Requests on Mount

Running a POST request in an Effect that fires twice in development can cause duplicate logs. Keep the code as‑is but be aware that in production it runs only once.

7. Chains of Effects

Chaining Effects that each set state causes multiple re‑renders and is hard to maintain. Prefer calculating everything during render and performing state updates in a single event handler.

function Game() {
  const [card, setCard] = useState(null);
  const [goldCardCount, setGoldCardCount] = useState(0);
  const [round, setRound] = useState(1);
  const isGameOver = round > 5;
  function handlePlaceCard(nextCard) {
    if (isGameOver) throw Error('Game already ended.');
    setCard(nextCard);
    if (nextCard.gold) {
      if (goldCardCount <= 3) {
        setGoldCardCount(goldCardCount + 1);
      } else {
        setGoldCardCount(0);
        setRound(round + 1);
        if (round === 5) alert('Good game!');
      }
    }
  }
}

8. One‑Time App Initialization

Instead of an Effect that runs on every component mount, track initialization with a module‑level flag or run the code before the app renders.

let didInit = false;
function App() {
  useEffect(() => {
    if (!didInit) {
      didInit = true;
      loadDataFromLocalStorage();
      checkAuthToken();
    }
  }, []);
}

9. Notifying Parent of State Changes

Calling onChange from an Effect adds an extra render cycle. Instead, invoke the callback directly in the event that changes the state.

function Toggle({ isOn, onChange }) {
  function handleClick() { onChange(!isOn); }
  function handleDragEnd(e) {
    if (isCloserToRightEdge(e)) onChange(true);
    else onChange(false);
  }
}

10. Passing Data to Parent

Data flow should be parent‑to‑child. Let the parent fetch data and pass it down, rather than having the child push data upward via an Effect.

function Parent() {
  const data = useSomeAPI();
  return <Child data={data} />;
}
function Child({ data }) { /* render */ }

11. Subscribing to External Stores

Manual subscription inside an Effect works but is error‑prone. Use the built‑in useSyncExternalStore hook.

function useOnlineStatus() {
  return useSyncExternalStore(
    (callback) => {
      window.addEventListener('online', callback);
      window.addEventListener('offline', callback);
      return () => {
        window.removeEventListener('online', callback);
        window.removeEventListener('offline', callback);
      };
    },
    () => navigator.onLine,
    () => true
  );
}

12. Fetching Data

Fetching data is a valid use of Effects, but you must handle cleanup to avoid race conditions. A custom hook can encapsulate the logic.

function useData(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    let ignore = false;
    fetch(url)
      .then(r => r.json())
      .then(json => { if (!ignore) setData(json); });
    return () => { ignore = true; };
  }, [url]);
  return data;
}
function SearchResults({ query }) {
  const [page, setPage] = useState(1);
  const params = new URLSearchParams({ query, page });
  const results = useData(`/api/search?${params}`);
  function handleNextPageClick() { setPage(page + 1); }
}

Summary

useEffect is a powerful tool for bridging React's declarative model with imperative side effects. Use it only when the effect truly needs to run after the component is displayed. In many cases you can compute values during render, cache with useMemo, lift state, or employ dedicated hooks, leading to clearer, more efficient code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ReactuseEffect
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.