unnecessary effects
useEffect is not a lifecycle hook
useEffect exists to synchronize with external systems (APIs, DOM, timers). Using it to transform data or compute state in response to other state is the most common pattern that causes double renders, sync bugs, and hard-to-follow code.
When NOT to use useEffect
If the code inside the effect only reads props or state and updates other state, it doesn't need an effect. Do it during render: compute the derived value directly as a variable. The result is the same but without the extra render the effect produces after the first render.
When to use useEffect
Reserve useEffect for real synchronization with the outside world: fetch, subscriptions to DOM or external API events, direct DOM node manipulation, or integration with third-party libraries that operate outside the React tree.
Common pitfalls
- 01useEffect with setState always produces at least one extra render: the initial one with stale state, and another when the effect updates it.
- 02Chaining effects (one effect updates state that triggers another effect) is almost always a sign that the logic should live in the render or an event handler.
- 03fetch inside useEffect without cleanup can update state of an already-unmounted component — always return a function that cancels the request or ignores the response.
