no-use-effect
GitHub指导在React开发中避免滥用useEffect,通过派生状态、数据查询库或事件处理等模式替代副作用,提升代码质量。
Trigger Scenarios
Install
npx skills add fastrepl/anarlog --skill no-use-effect -g -y
SKILL.md
Frontmatter
{
"name": "no-use-effect",
"description": "Avoid direct React `useEffect` usage when writing or reviewing React components and hooks. Use this skill when creating new React code, refactoring existing `useEffect` calls, reviewing PRs that introduce `useEffect`, or when an agent reaches for `useEffect` as a default. Replace effects with derived state, query or mutation libraries, event handlers, `useMountEffect` for mount-only external sync, or `key`-based remounts."
}
No useEffect
Never call useEffect directly unless the work is a true mount-only sync with an external system and no simpler pattern fits. Prefer a render-time computation, a query or mutation hook, an event handler, or a remount boundary instead.
Workflow
- Classify why the effect exists.
- Replace it with the narrowest pattern below.
- Verify with the project's normal lint, typecheck, and tests.
Replacement Patterns
1. Derive state, do not sync it
Replace useEffect(() => setX(derive(y)), [y]) with inline computation or the project's existing memoization conventions.
const filteredProducts = products.filter((product) => product.inStock);
Smell test: state only mirrors props or other state.
2. Use query or mutation libraries for async work
Replace fetch-in-effect code with the app's data layer.
const productQuery = useQuery({
queryKey: ["product", productId],
queryFn: () => fetchProduct(productId),
});
Smell test: the effect fetches, awaits, retries, cancels, or writes async results into local state.
3. Use event handlers for user actions
Do the work where the action happens instead of setting a flag and reacting later.
const handleLike = () => {
postLike();
};
Smell test: state exists only to trigger an effect, then gets reset.
4. Use useMountEffect for mount-only external sync
Use this only for work that is naturally "set up on mount, clean up on unmount": DOM integration, subscriptions, third-party widgets, and stable singleton bindings.
If the project already has a useMountEffect, use it. If not, add a small wrapper:
import { useEffect } from "react";
export function useMountEffect(effect: () => void | (() => void)) {
useEffect(effect, []);
}
Prefer changing component structure so the component only mounts once preconditions are satisfied.
Smell test: the effect is a one-time setup or teardown for something outside React.
5. Use key to reset component state
If a component should behave like a fresh instance when an ID or entity changes, remount it from the parent.
return <VideoPlayer key={videoId} videoId={videoId} />;
Smell test: the effect exists only to reset local state when props change.
Review Standard
Treat new direct useEffect usage as a bug by default. Ask which of the five patterns applies. Accept direct useEffect only with explicit justification that the code is a mount-only external sync and that useMountEffect is the intended escape hatch.
Structure React Code This Way
Order component logic like this:
- Hooks that read external state or data.
- Local state.
- Derived values computed during render.
- Event handlers.
- Early returns.
- Render output.
Do not introduce mirrored state just to make an effect possible.
Version History
- 42f3624 Current 2026-09-28 18:50


