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.
95
95%
Does it follow best practices?
Impact
98%
1.02xAverage score across 4 eval scenarios
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.react-doctor CLI diff scan when available, plus the smallest runtime or component check that exercises the changed path. Fix failures and rerun before completion.Use the highest applicable layer:
useSyncExternalStore or a reviewed domain-specific external-system hook with explicit reactive inputsIf none of these fit, stop and explain why the code truly needs an effect instead of adding direct useEffect.
Prefer an existing repo integration hook. If the repo has no standard, add a domain-specific hook that names the external system, owns its setup and cleanup, accepts every reactive input explicitly, and keeps those inputs in the effect dependency list. See the external-system replacement for the concrete shape.
Do not expose a generic effect callback or dependency list to callers, and do not suppress react-hooks/exhaustive-deps. A truly mount-only integration may use an empty dependency list only when its setup reads no reactive value from props, state, or the component closure. Prefer useSyncExternalStore for external stores or browser values that change over time. Never use an exception hook to 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 reviewed external-integration hooks. Also block namespace calls such as React.useEffect(...) with no-restricted-syntax or a custom rule. Allow only named, reviewed external-integration hook files as direct-import/call exceptions; do not create a general-purpose wrapper exemption. If the repo already uses another lint shape, preserve that local convention.
{
"no-restricted-imports": ["error", {
name: "react",
importNames: ["useEffect"],
message: "Use a declarative replacement or reviewed external-integration hook.",
}],
"no-restricted-syntax": ["error", {
selector: "CallExpression[callee.object.name='React'][callee.property.name='useEffect']",
message: "Do not call React.useEffect directly.",
}],
}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.