{
  "id": "ai-overuses-useeffect",
  "type": "failures",
  "category": "failures",
  "locale": "en",
  "url": "/failures/ai-overuses-useeffect",
  "title": "How to Fix AI Overusing useEffect",
  "description": "AI agents reach for useEffect to handle derived state, event transforms, and data fetching that should instead use useMemo, event handlers, or Server Components.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "TypeScript"
  ],
  "tags": [
    "react",
    "nextjs",
    "hydration"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "The agent wraps logic in `useEffect` that should be derived state, an event\nhandler, or a Server Component fetch — causing extra renders, stale closures,\nand race conditions.\n\n## The symptom\n\n`useEffect` is used to compute derived values or synchronize state that could\nbe a plain calculation.\n\n```tsx\n\"use client\";\nimport { useState, useEffect } from \"react\";\n\n// WRONG — useEffect to compute derived state\nexport function CartSummary({ items }: { items: CartItem[] }) {\n  const [total, setTotal] = useState(0);\n\n  useEffect(() => {\n    setTotal(items.reduce((sum, item) => sum + item.price * item.qty, 0));\n  }, [items]);\n\n  return <p>Total: ${total}</p>;\n  // Renders twice: once with total=0, once after the effect runs\n}\n```\n\nA second pattern: fetching data in `useEffect` when a Server Component could\ndo it without any client bundle cost.\n\n```tsx\n\"use client\";\nuseEffect(() => {\n  fetch(\"/api/products\").then((r) => r.json()).then(setProducts);\n}, []);\n// Waterfall: page loads -> JS executes -> fetch starts -> render\n```\n\n## Why it happens\n\n`useEffect` was the primary side-effect escape hatch in React 16/17. Models\ntrained on pre-React-18 and pre-App-Router code learned to reach for it by\ndefault. The mental model \"I need something to happen after render\" maps to\n`useEffect` even when the real fix is simpler.\n\n## How to spot it\n\n- `useEffect` sets state that is directly computable from props or other state.\n- `useEffect(() => { fetch(...).then(setState) }, [])` at the top of a\n  component that is not inside a Server Component subtree.\n- `useEffect` with no cleanup where the dependency is a function (stale closure\n  risk).\n- `useEffect` used purely for logging or analytics on mount.\n\n## How to fix it\n\nUse the right tool for the job:\n\n```tsx\n// CORRECT — derived state: just compute it\nexport function CartSummary({ items }: { items: CartItem[] }) {\n  const total = items.reduce((sum, item) => sum + item.price * item.qty, 0);\n  return <p>Total: ${total.toFixed(2)}</p>;\n}\n\n// CORRECT — expensive derivation: useMemo\nimport { useMemo } from \"react\";\n\nexport function FilteredList({ items, query }: { items: Item[]; query: string }) {\n  const filtered = useMemo(\n    () => items.filter((i) => i.name.toLowerCase().includes(query.toLowerCase())),\n    [items, query]\n  );\n  return <ul>{filtered.map((i) => <li key={i.id}>{i.name}</li>)}</ul>;\n}\n```\n\n```tsx\n// CORRECT — data fetching: async Server Component (no useEffect needed)\n// app/products/page.tsx\nexport default async function ProductsPage() {\n  const products = await fetch(\"https://api.example.com/products\").then((r) =>\n    r.json()\n  );\n  return <ProductList products={products} />;\n}\n```\n\n```txt\n[ ] Derived values from props/state are plain calculations, not useEffect + setState\n[ ] Expensive derivations use useMemo, not useEffect\n[ ] Data fetching on mount moves to async Server Components or React Query/SWR\n[ ] Event-driven side-effects (form submit, button click) are in event handlers\n[ ] useEffect is reserved for: external system sync, subscriptions, cleanup\n[ ] Every useEffect that sets state has a loading/error state and cleanup\n```\n\n## Fix Prompt\n\n```txt title=\"Fix Prompt\"\nThis component uses useEffect to compute derived state or fetch initial data.\nRefactor it: replace derived-state effects with direct calculations or useMemo,\nmove initial data fetching to an async Server Component parent or to a\ndata-fetching library (SWR/React Query) with proper loading/error handling, and\nkeep useEffect only for genuine external-system synchronization that requires\ncleanup. Explain each useEffect you keep and why it cannot be replaced.\n```\n\n## Test\n\n```bash\n# Count useEffect calls — a high number is a smell worth reviewing\ngrep -rn \"useEffect\" --include=\"*.tsx\" --include=\"*.ts\" src/ app/ \\\n  | grep -v \"node_modules\" \\\n  | wc -l\n\n# Flag useEffect+setState patterns for manual review\ngrep -A5 \"useEffect\" app/**/*.tsx 2>/dev/null | grep \"setState\\|set[A-Z]\" \\\n  && echo \"REVIEW: possible derived-state antipattern\" || echo \"OK\"\n```"
}