Skip to content

Navid's Blog

Ideas, Experiments, and Lessons Learned

Menu
Menu

Why I Stopped Using useEffect (And What I Use Instead)

Posted on March 30, 2026 by Navid

I used to reach for useEffect for everything. Data fetching? useEffect. Form validation? useEffect. Syncing state? useEffect. My components looked like chaos, and I couldn’t figure out why my code felt so hard to maintain.

Then I learned something that changed how I write React: useEffect is not the answer for most problems.

The Problem with useEffect

Here’s the thing about useEffect — it runs after every render by default. That means if you’re not careful, you’re creating infinite loops, racing conditions, and state that gets out of sync.

I spent hours debugging why my API was being called 10 times. Or why my form reset unexpectedly. The culprit was almost always useEffect doing something I didn’t intend.

What I Use Instead

1. Data Fetching: React Query or SWR

Stop fetching data in useEffect. Libraries like React Query (or TanStack Query) handle caching, refetching, loading states, and errors much better than anything you can write with useEffect.

// Before (useEffect)
useEffect(() => {
  fetch('/api/users').then(setUsers);
}, []);

// After (React Query)
const { data: users } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });

2. Derived State: Just Calculate It

If you have state that depends on other state, don’t sync it with useEffect. Just compute it during render.

// Before (useEffect)
const [total, setTotal] = useState(0);
useEffect(() => {
  setTotal(items.reduce((sum, item) => sum + item.price, 0));
}, [items]);

// After
const total = items.reduce((sum, item) => sum + item.price, 0);

3. Event Handlers: Handle Events Directly

Don’t use useEffect to respond to user actions. Use the handlers directly.

// Before
useEffect(() => {
  const handleKeyDown = (e) => { ... };
  window.addEventListener('keydown', handleKeyDown);
  return () => window.removeEventListener('keydown', handleKeyDown);
}, []);

// After - use useEffect only when truly needed for subscriptions

When useEffect Actually Makes Sense

Look, I’m not saying never use useEffect. It has valid uses:

  • Subscribing to external systems (WebSockets, browser APIs)
  • Manually interacting with the DOM
  • Logging analytics (though even that can be debatable)

But for most business logic in your components? There’s usually a cleaner way.

The Shift That Helped Me

Instead of asking “when does this code run?” I started asking “what does this code represent?”

If it’s data, use a data-fetching library. If it’s UI state, keep it simple. If it’s derived from props, compute it.

The result? My components got shorter, bugs got fewer, and I stopped fearing the React DevTools profiler.

Try it. Replace one complicated useEffect with the right tool and see how it feels.

Categories

  • AI Experiments
  • Coding
  • Debugging Stories
  • Hot Takes
  • Ideas
  • Lessons Learned
  • Project Management
  • Uncategorized
  • Vibe Coding

Recent Posts

  • My Team Spent 2 Weeks Replacing Our Authentication — Here’s What Happened
  • My Team Spent 2 Weeks Replacing Our Authentication — Here’s What Happened
  • I Deleted 2000 Lines of Code and Everything Still Worked
  • Why I Stopped Using useEffect (And What I Use Instead)
  • Why I Stopped Using useEffect (And What I Use Instead)