Stop Using useEffect for Everything (Try Lazy Effects Instead)

By Ammiel Yawson
24 June 2025·2 mins read
React
Stop Using useEffect for Everything (Try Lazy Effects Instead)

The best code tells a clear story: user acts → handler responds.

useEffect can break this story by separating effects from their actions, removing the direct link between user interactions and their responses. While it's the most common way we're taught to handle side effects in React, not all effects benefit from automatic execution.

When the useEffect hook is overused, it can lead to cognitive load when looking to link effects to their causing actions.

As a developer, a huge part of your work is reading code to understand and either extend functionality or fix bugs. Hence, you always want to lean on decisions that lead to clearer code.

In the case of effects, lazy effects (sometimes known event-driven effects) are how you make sure you keep a direct link between your actions and their effects.

Lazy effects are side effects called in response to user events/actions. They're "lazy" because they wait until manually called, and "event-driven" because they respond to user interactions.

Let’s look at examples:

Search example

// ❌ Reactive useEffect
useEffect(() => {
  if (searchTerm.length > 2) {
    searchUsers(searchTerm);
  }
}, [searchTerm]);

// ✅ Lazy event-driven
const handleSearch = (term) => {
  setSearchTerm(term);
  if (term.length > 2) {
    searchUsers(term);
  }
};

The searchUsers function is clearly called inside handleSearch, establishing a direct relation between when search handler and the search users effect.


Modal example

// ❌ Reactive
useEffect(() => {
  if (isModalOpen) {
    trackModalView('user-profile');
  }
}, [isModalOpen]);

// ✅ Lazy
const openModal = () => {
  setIsModalOpen(true);
  trackModalView('user-profile');
};

Same as in the search example, There's a direct link between openModal handler and trackModalView effect.

How do you decide when to use lazy effects over useEffect?

The simple question to ask is: does it occur in response to a user action? If yes, then you mostly want to go with event driven effects. If not, then useEffect is probably the right choice for reactive updates like data syncing or cleanup operations.