P PasteCode
Failure

How to Fix AI Overusing useEffect

AI agents reach for useEffect to handle derived state, event transforms, and data fetching that should instead use useMemo, event handlers, or Server Components.

CursorClaude CodeCodexWindsurf Next.jsTypeScript
.md .json Updated Jun 8, 2026

The agent wraps logic in useEffect that should be derived state, an event handler, or a Server Component fetch — causing extra renders, stale closures, and race conditions.

The symptom

useEffect is used to compute derived values or synchronize state that could be a plain calculation.

"use client";
import { useState, useEffect } from "react";
// WRONG — useEffect to compute derived state
export function CartSummary({ items }: { items: CartItem[] }) {
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, item) => sum + item.price * item.qty, 0));
}, [items]);
return <p>Total: ${total}</p>;
// Renders twice: once with total=0, once after the effect runs
}

A second pattern: fetching data in useEffect when a Server Component could do it without any client bundle cost.

"use client";
useEffect(() => {
fetch("/api/products").then((r) => r.json()).then(setProducts);
}, []);
// Waterfall: page loads -> JS executes -> fetch starts -> render

Why it happens

useEffect was the primary side-effect escape hatch in React 16/17. Models trained on pre-React-18 and pre-App-Router code learned to reach for it by default. The mental model “I need something to happen after render” maps to useEffect even when the real fix is simpler.

How to spot it

  • useEffect sets state that is directly computable from props or other state.
  • useEffect(() => { fetch(...).then(setState) }, []) at the top of a component that is not inside a Server Component subtree.
  • useEffect with no cleanup where the dependency is a function (stale closure risk).
  • useEffect used purely for logging or analytics on mount.

How to fix it

Use the right tool for the job:

// CORRECT — derived state: just compute it
export function CartSummary({ items }: { items: CartItem[] }) {
const total = items.reduce((sum, item) => sum + item.price * item.qty, 0);
return <p>Total: ${total.toFixed(2)}</p>;
}
// CORRECT — expensive derivation: useMemo
import { useMemo } from "react";
export function FilteredList({ items, query }: { items: Item[]; query: string }) {
const filtered = useMemo(
() => items.filter((i) => i.name.toLowerCase().includes(query.toLowerCase())),
[items, query]
);
return <ul>{filtered.map((i) => <li key={i.id}>{i.name}</li>)}</ul>;
}
app/products/page.tsx
// CORRECT — data fetching: async Server Component (no useEffect needed)
export default async function ProductsPage() {
const products = await fetch("https://api.example.com/products").then((r) =>
r.json()
);
return <ProductList products={products} />;
}
[ ] Derived values from props/state are plain calculations, not useEffect + setState
[ ] Expensive derivations use useMemo, not useEffect
[ ] Data fetching on mount moves to async Server Components or React Query/SWR
[ ] Event-driven side-effects (form submit, button click) are in event handlers
[ ] useEffect is reserved for: external system sync, subscriptions, cleanup
[ ] Every useEffect that sets state has a loading/error state and cleanup

Fix Prompt

Fix Prompt
This component uses useEffect to compute derived state or fetch initial data.
Refactor it: replace derived-state effects with direct calculations or useMemo,
move initial data fetching to an async Server Component parent or to a
data-fetching library (SWR/React Query) with proper loading/error handling, and
keep useEffect only for genuine external-system synchronization that requires
cleanup. Explain each useEffect you keep and why it cannot be replaced.

Test

Terminal window
# Count useEffect calls — a high number is a smell worth reviewing
grep -rn "useEffect" --include="*.tsx" --include="*.ts" src/ app/ \
| grep -v "node_modules" \
| wc -l
# Flag useEffect+setState patterns for manual review
grep -A5 "useEffect" app/**/*.tsx 2>/dev/null | grep "setState\|set[A-Z]" \
&& echo "REVIEW: possible derived-state antipattern" || echo "OK"