Avoid Unnecessary useEffect
Treat effects as an escape hatch for synchronizing React with an external system. Before adding or preserving an effect, prefer logic during rendering or in the event handler that caused the change.
Decide Where Logic Belongs
- Calculate values derived from props or state during render. Do not store derived values in state merely to update them from an effect.
- Handle clicks, submissions, selections, dragging, and typing in their corresponding event handlers.
- Use props directly unless the component intentionally needs an independent editable snapshot.
- Keep one source of truth. Store the minimum state and derive related values.
- Filter, sort, map, and otherwise transform data during render. Use
useMemoonly when the computation is meaningfully expensive or referential stability is required. - Prefer framework-native loaders, Server Components, router loaders, TanStack Query, RTK Query, or the project's established server-state solution over manually recreating caching, retries, deduplication, and refetching in an effect. Keep a manual fetch effect only when it is genuinely the appropriate abstraction.
Use useEffect when the component must synchronize with something outside React, including browser or imperative DOM APIs, WebSockets, timers, subscriptions, analytics, external event listeners, and third-party imperative libraries. Include cleanup when that synchronization creates a subscription, listener, timer, or resource.
Review Heuristic
When encountering this shape:
useEffect(() => {
setSomething(computeValue(inputs));
}, [inputs]);
Check whether it can become:
const something = computeValue(inputs);
Do not apply this rewrite mechanically when the effect truly synchronizes an external system or when state intentionally represents an independently editable snapshot.
Mental Model
Prefer:
React props/state -> synchronize -> external system
Avoid:
React state A -> effect -> React state B
Before using an effect, ask:
- Is this needed for rendering? Calculate it during render.
- Was it caused by a user action? Put it in the event handler.
- Can it be derived from props or existing state? Derive it.
- Does it synchronize two React state values? Use one source of truth.
- Is it a data transformation? Perform it during render, optionally memoizing when justified.
- Is it server data? Use the project's server-state or framework abstraction when available.
- Does it connect React to an external system? An effect is likely appropriate.