| name | analyze-effects |
|---|---|
| description | Identify all useEffect hooks in a folder and classify them, then apply the correct replacement pattern from "You Might Not Need an Effect" |
| argument-hint | [folder-path] |
Analyze all useEffect (and useLayoutEffect) hooks in the specified folder and recommend the safest replacement patterns.
Target folder: $ARGUMENTS
If no folder is provided, ask the user which folder to analyze.
Do not edit code as part of this skill unless the user explicitly asks for a refactor after reviewing the analysis.
Find every useEffect and useLayoutEffect in the target folder (excluding test files). Read each file fully before classifying any effect. For each one, classify it into one of these categories:
| Category | Description |
|---|---|
| Deriving state | Sets state based on other state/props — usually unnecessary |
| Fetching data | Calls an API and sets state with the result |
| Responding to event | Does work that should happen in an event handler instead |
| Syncing with external system | DOM manipulation, subscriptions, browser APIs, third-party widgets |
| Resetting state on prop change | Resets local state when a prop/dependency changes |
For each effect, report:
- File and line number
- Category from the table above
- What it does (one sentence)
- Verdict:
KEEP,REFACTOR, orINVESTIGATEKEEP— legitimate external sync or mount-only setupREFACTOR— can be replaced with a better patternINVESTIGATE— needs deeper analysis or discussion
For each effect marked REFACTOR, recommend the best replacement using these five rules:
- Prefer patterns that already exist in the codebase.
- Do not invent custom hooks, helper APIs, or new libraries unless they are already present in the repo or the user asked for that kind of change.
- If more than one replacement is plausible, explain the tradeoff briefly and prefer
INVESTIGATEover overconfident advice.
Most effects that set state from another state are unnecessary and add extra renders.
// BAD: Two render cycles
useEffect(() => {
setFilteredProducts(products.filter((p) => p.inStock));
}, [products]);
// GOOD: Compute inline
const filteredProducts = products.filter((p) => p.inStock);Smell test: useEffect(() => setX(deriveFromY(y)), [y]), or state that only mirrors other state or props.
Effect-based fetching creates race conditions and duplicated caching logic.
// BAD: Race condition risk
useEffect(() => {
fetchProduct(productId).then(setProduct);
}, [productId]);
// GOOD: Reuse the project's existing loader/query pattern
const { data: product } = useQuery(['product', productId], () =>
fetchProduct(productId)
);Smell test: Effect does fetch(...) then setState(...), or re-implements caching/retries/cancellation.
Prefer framework loaders, Server Components, or an existing query library when the repo already uses them. If the project has no established pattern, recommend INVESTIGATE instead of prescribing a new dependency.
If a user action triggers the work, do it in the handler.
// BAD: Effect as an action relay
useEffect(() => {
if (liked) { postLike(); setLiked(false); }
}, [liked]);
// GOOD: Direct event-driven action
<button onClick={() => postLike()}>Like</button>Smell test: State used as a flag so an effect can do the real action.
Good uses: DOM integration (focus, scroll), third-party widget lifecycles, browser API subscriptions.
// GOOD: Mount-only external setup with cleanup
useEffect(() => {
connectionManager.on('connected', handleConnect);
return () => connectionManager.off('connected', handleConnect);
}, []);Smell test: Synchronizing with an external system, behavior is "setup on mount, cleanup on unmount."
Only recommend project-specific helpers such as useMountEffect if they already exist in the codebase.
// BAD: Effect resets local state on prop change
useEffect(() => { setComment(''); }, [videoId]);
// GOOD: key forces clean remount
<CommentBox key={videoId} videoId={videoId} />Smell test: Effect's only job is resetting local state when an ID/prop changes.
Present results as a table first (summary), then detailed analysis for each REFACTOR item with the recommended code change. Group by file.
Do NOT propose changes to code you haven't read. Read each file fully before analyzing. Do NOT claim a refactor is safe unless the replacement fits the repo's current patterns and constraints.