CtrlK
BlogDocsLog inGet started
Tessl Logo

uinaf/react-ban-use-effect

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

Quality

90%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.md

name:
react-ban-use-effect
description:
Ban direct `useEffect` in React code. Use when writing, refactoring, reviewing, or migrating React components or hooks that import, call, add, or replace direct `useEffect`; when an agent reaches for effects for derived state, fetching, event reactions, resets, or external sync; or when adding lint/agent rules for a no-direct-useEffect policy. Do not use for ordinary React work with no effect smell, non-React code, or legitimate effect architecture outside React.

React Ban useEffect

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.

Start Here

  1. Search the touched React surface for direct useEffect imports and calls.
  2. Classify each effect by intent before editing:
    • render-time derivation
    • data fetching or server state
    • response to a user action
    • local state reset on identity change
    • async UI, pending state, or request waterfall
    • external-system synchronization
  3. Replace it with the narrowest declarative pattern from references/replacements.md.
  4. For data, forms, external stores, or performance work, also check the stronger alternative map in references/alternatives.md.
  5. Keep behavior proof concrete: run the repo's lint/type/test gate, React Doctor diff scan when available, plus the smallest runtime or component check that exercises the changed path.

Replacement Ladder

Use the highest applicable layer:

  1. render-time calculation
  2. server, loader, or framework data API
  3. server-state library such as TanStack Query, SWR, Relay, or Apollo
  4. event handler, form action, or mutation
  5. keyed component boundary
  6. useSyncExternalStore, approved mount sync, or a reviewed dependency-aware external-sync exception

If none of these fit, stop and explain why the code truly needs an effect instead of adding direct useEffect.

Allowed Escape Hatches

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.

Enforcement

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.

Boundaries

  • Scope the change to the touched path or requested migration slice.
  • Preserve the repo's existing data, lint, hook, and framework conventions.
  • Leave useLayoutEffect, framework lifecycle APIs, and non-React effect systems alone unless requested.
  • Use performance primitives only for real UI/perf evidence, React Doctor findings, or established repo patterns.

Sources

Core sources and the broader alternatives bibliography live in references/alternatives.md.

SKILL.md

tile.json