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
Default stance: do not import or call useEffect directly in React components. Treat effects as an escape hatch for synchronizing with external systems, not as the default place to put render, event, data, or reset logic.
useEffect imports and calls.Use the highest applicable layer:
useSyncExternalStore, approved mount sync, or a reviewed dependency-aware external-sync exceptionIf none of these fit, stop and explain why the code truly needs an effect instead of adding direct useEffect.
Prefer existing repo wrappers. If the repo has no standard, add mount-only sync close to shared React hooks:
import { useEffect } from "react";
export function useMountEffect(effect: () => void | (() => void)) {
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(effect, []);
}Good mount uses are browser API setup, DOM focus/scroll integration, and third-party widget lifecycles. Prefer useSyncExternalStore for external stores or browser values that change over time. If an external system must resync when a prop or state value changes, require an explicit reviewed exception or dependency-aware wrapper that keeps dependencies honest. Do not use wrappers to hide dependency problems, fetch server state, copy props into state, or relay user actions.
For new policy work, prefer the repo's existing ESLint shape. The usual rule is no-restricted-imports against useEffect from react, with the message pointing developers to declarative replacements and approved wrappers. Also block namespace calls such as React.useEffect(...) with no-restricted-syntax or a custom rule. Allow shared wrapper files as the only direct-import/call exceptions. If the repo already uses another lint shape, preserve that local convention.
If the repo uses React Doctor, prefer its native rules for effect smells (no-fetch-in-effect, no-derived-state-effect) in addition to the import ban. Keep suppressions line-local and rule-specific.
For reviews, treat new direct useEffect as a finding unless the diff also introduces a clear, reviewed exception. Ask for a replacement plan rather than dependency-array tuning.
For upstream provenance and uinaf tailoring notes, use references/upstream.md.
useLayoutEffect, framework lifecycle APIs, and non-React effect systems alone unless requested.Core sources and the broader alternatives bibliography live in references/alternatives.md.