Ban direct `useEffect` in React components. Use when writing, refactoring, or reviewing React code so derived state, data fetching, user actions, resets, and mount-only external synchronization use declarative replacement patterns instead of dependency-array choreography.
72
90%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Use this after the basic replacement guide when an effect is hiding a bigger React architecture problem.
Choose the highest available layer:
TanStack Query replacements should usually include:
queryOptions or the repo's existing query factory patternenabled for dependent queries instead of conditional hooks or effect flagsselect for deriving server-state viewssignal to fetchuseMutation for writes, followed by invalidation or cache updatesonMutate cancellation and rollback context for optimistic updatesplaceholderData for pagination instead of copying previous pages into local stateUse regular useQuery instead of Suspense query hooks when dependent enabled gates, placeholder pagination, or cancellation semantics matter. Suspense is good for boundary-driven loading, not for every query replacement.
If the work happens because the user clicked, typed, submitted, dragged, or navigated, keep it at that cause.
Prefer:
useActionState for action result/error stateuseFormStatus for submit pending UI in a child component rendered inside the formuseOptimistic for local optimistic UI when updates are dispatched from an action or startTransitionuseMutation for client server-state writesAvoid setFlag(true) followed by an effect that notices the flag. That splits cause from effect and creates stale-closure and dependency-array risk.
If the component needs a changing value from outside React, prefer useSyncExternalStore over an effect that subscribes and copies into local state.
Good candidates:
Keep subscribe stable outside the component when possible. getSnapshot must return a stable immutable snapshot while the external value has not changed.
Effects often hide performance work that should be explicit.
Prioritize high-impact fixes first: waterfalls, bundle/server boundaries, and data ownership usually beat low-level memoization. Do not jump to useMemo while a request waterfall or client-only fetch is still dominating the path.
Prefer:
Promise.alluseDeferredValue for slow children that can lag behind urgent inputuseTransition for non-urgent updates and navigationsuseState initialization for expensive initial local valuesOnly apply performance primitives when they map to a real bottleneck, React Doctor diagnostic, or existing repo standard.
Run the repo's normal guardrails first. When React Doctor is available, add:
npx react-doctor@latest --verbose --diffUse React Doctor's explanation mode for unclear diagnostics or suppressions:
npx react-doctor@latest --explain src/App.tsx:42Relevant effect-adjacent diagnostics include derived-state effects, fetch-in-effect, missing cleanup, stale closure capture, async without cleanup, exhaustive dependencies, and giant components created by trying to centralize too much orchestration in one component.
Suppress only when the code is intentionally unusual, and keep suppressions line-local and rule-specific.