You might not need a useEffect

You Might Not Need an Effect

I've been using React since hooks came out, and I've seen a lot of pitfalls with useEffect. Infinite render loops, dependency arrays that I'd just slap eslint-disable on because I didn't understand why the linter was complaining, and components that had like 5 different Effects all triggering each other. I learned the hard way that Effects should be used sparingly, but I never really understood when they were actually necessary versus when I was just overcomplicating things.

When Effects Are Actually Needed

Effects exist to synchronize your components with external systems - things like jQuery widgets, network requests, or browser APIs. If there's no external system involved, you probably don't need an Effect. The two main scenarios where developers incorrectly use Effects are transforming data for rendering and handling user events. Both of these should happen without Effects.

Calculate During Render Instead

One of the biggest takeaways is that derived state should just be calculated during render. If you have firstName and lastName in state, don't create a fullName state variable and update it in an Effect - just calculate it by concatenating them together. This is faster, simpler, and prevents synchronization bugs. For expensive calculations, wrap them in useMemo instead of putting them in state with Effects.

Event Handlers vs Effects

The key question is: why does this code need to run? If it's because the user clicked a button, put it in the event handler. If it's because the component was displayed, put it in an Effect. For example, showing a notification when a user buys a product belongs in the button's click handler, not in an Effect that watches whether the product is in the cart. Effects that watch state to perform actions are usually a code smell.

Resetting State with Keys and Understanding Dependencies

Instead of using Effects to reset state when props change, pass a key prop to your component. When the key changes, React treats it as a completely new component instance and resets all its state automatically. This is way cleaner than Effects watching props and calling setState.

One thing that caught me out constantly with dependency arrays: React compares dependencies by reference, not by value. If you put an array in your dependency array, it won't trigger the Effect when the contents change - only when the array reference itself changes. So if you're creating a new array on every render, your Effect runs every time. If you're passing the same array reference with different contents, your Effect never runs.

The Real Takeaway

The article shows that most Effect chains - where one Effect triggers another state update which triggers another Effect - can be replaced with calculations during render or logic in event handlers. Those infinite render loops I used to get? Usually from Effect chains or missing cleanup functions. The dependency array issues? Usually from trying to use Effects when I should have been using event handlers or calculating during render. Effects should be rare. If you're writing a lot of Effects, step back and think about whether you're synchronizing with an external system or just making your code more complex.

Resources

https://react.dev/learn/you-might-not-need-an-effect

https://react.dev/learn/synchronizing-with-effects

https://react.dev/reference/react/useSyncExternalStore