Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

React Hooks Antipatterns Skill

ASecurity

Detect and fix React hooks anti-patterns — stale useState from props, StrictMode double-execution, dependency traps, stale refs, toast.promise leaks.

6 stars
0 votes
0 copies
0 views
Added 9/20/2026
developmenttypescriptgobashreactnextjsdebugginggitapifrontendperformance

Works with

cliapi

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add darellchua2/opencode-config-template --skill react-hooks-antipatterns-skill --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Hooks Antipatterns Skill?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for React Hooks Antipatterns Skill
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/darellchua2-react-hooks-antipatterns-skill/badge)](https://www.skillsdirectory.com/skills/darellchua2-react-hooks-antipatterns-skill)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: react-hooks-antipatterns-skill
description: >-
  Detect and fix React hooks anti-patterns — stale useState from props,
  StrictMode double-execution, dependency traps, stale refs, toast.promise
  leaks.
license: Apache-2.0
compatibility: opencode
category: Framework-Specific
---

<!-- Provenance: canvastekk-frontend-nextjs LEARNINGS. Split from react-nextjs-antipatterns-skill. PLAN-GIT-312. -->

## What I do

I detect and fix anti-patterns specific to React hooks that cause production incidents:

1. **State & Ref Pitfalls**: Stale derived state, StrictMode double-execution, stale ref accumulators
2. **Dependency Trap Anti-Patterns**: useCallback/useMemo dependency issues that cause unnecessary recreation or stale closures
3. **Async Hook Pitfalls**: toast.promise double-consumer Sentry noise
4. **Recommended Pattern**: Hook decomposition for complex components

## When to use me

Use this skill when:
- Debugging stale state after external data mutations
- Investigating StrictMode double-execution bugs
- Fixing useCallback/useMemo dependency warnings or stale closures
- Auditing toast.promise usage for double-error-reporting
- Decomposing large components into focused hooks
- Reviewing React hooks code for production-readiness

## Related Skills

- **react-render-antipatterns-skill**: Peer — covers render-time anti-patterns (fragment keys, JSON.parse in handlers, visibility toggle inconsistencies, theme-driven design). This skill covers hook lifecycle anti-patterns.
- **typescript-dry-principle-skill**: Duplicate type definitions and status mappings (redistributed from original skill).
- **performance-optimization-skill**: Module-scope cache leaks (redistributed from original skill).

---

## A. State & Ref Pitfalls

### A1. `derived-state-props-without-sync` — Stale useState from Props

`useState` initialized from props becomes stale when the parent updates the prop.

**Before (stale):**
```tsx
function EditDialog({ initialName }: { initialName: string }) {
  const [name, setName] = useState(initialName)
  // name stays "old" even after parent passes new initialName
}
```

**After (synced):**
```tsx
function EditDialog({ initialName }: { initialName: string }) {
  const [name, setName] = useState(initialName)

  useEffect(() => {
    setName(initialName)
  }, [initialName])
  // OR use a controlled component with `key={initialName}` to remount
}
```

### A2. `ref-guard-early-return` — StrictMode Double-Execution

`useRef(false)` guard with early returns before setting ref causes double-execution in React StrictMode.

**Before (buggy):**
```tsx
function useInit() {
  const started = useRef(false)

  useEffect(() => {
    if (someCondition) return // Early return BEFORE setting ref
    if (started.current) return
    started.current = true
    init() // Runs twice in StrictMode
  }, [])
}
```

**After (correct):**
```tsx
function useInit() {
  const started = useRef(false)

  useEffect(() => {
    if (started.current) return
    started.current = true // Set ref FIRST, before any logic
    if (someCondition) return
    init()
  }, [])
}
```

---

## B. Dependency Trap Anti-Patterns

### B1. `loading-state-in-usecallback-deps` — Unnecessary Callback Recreation

Including loading booleans in `useCallback` deps causes unnecessary recreation.

**Before (inefficient):**
```tsx
const handleSubmit = useCallback(() => {
  if (loading) return
  submit()
}, [loading, submit]) // Recreates on every loading toggle
```

**After (efficient):**
```tsx
const handleSubmit = useCallback(() => {
  submit()
}, [submit])

// Disable in the JSX instead
<button onClick={handleSubmit} disabled={loading}>Submit</button>
```

### B2. `inline-computed-usememo-dep` — New Reference Every Render

Inline ternary/computed values as `useMemo` deps create new references every render.

**Before (stale):**
```tsx
const sorted = useMemo(() => sortItems(items), [items.length > 0 ? items : []])
// Inline ternary creates new array reference each render — memo never hits
```

**After (correct):**
```tsx
const effectiveItems = useMemo(() => (items.length > 0 ? items : []), [items])
const sorted = useMemo(() => sortItems(effectiveItems), [effectiveItems])
```

### B3. `reset-refs-on-effect-restart` — Stale Ref Accumulators

`useRef` accumulators in `useEffect` carry over from previous lifecycles.

**Before (buggy):**
```tsx
const counter = useRef(0)

useEffect(() => {
  counter.current++ // Carries stale value from previous lifecycle — not reset
  doWork(counter.current)
}, [dependency])
```

**After (correct):**
```tsx
const counter = useRef(0)

useEffect(() => {
  counter.current = 0 // Reset at TOP of effect body
  counter.current++
  doWork(counter.current)
}, [dependency])
```

---

## C. Async Hook Pitfalls

### C1. `toast-promise-await-without-catch` — Double Consumer on toast.promise

`toast.promise(apiCall(), { ... })` and a bare `await apiCall()` are two INDEPENDENT consumers of the same promise. The toast wrapper handles the rejection (shows the error UI), but the bare `await` still lets the rejection propagate up the call stack. If the containing function is called from a path that has no try/catch, the unhandled rejection fires a Sentry alert for an error the user has already seen and dismissed. Add an empty `.catch(() => {})` to the awaited promise to swallow the already-surfaced error, OR drop the `await` entirely if only the toast UX is needed.

```tsx
import { toast } from 'sonner'

// BAD — toast handles the error UI, but the bare await re-surfaces it to Sentry
async function handleSubmit() {
  toast.promise(apiCall(), {
    loading: 'Saving...',
    success: 'Saved',
    error: 'Save failed',        // user sees this
  })
  await apiCall()                // rejection propagates AGAIN → Sentry noise
}

// GOOD (option A) — swallow the already-surfaced error
async function handleSubmit() {
  toast.promise(apiCall(), {
    loading: 'Saving...',
    success: 'Saved',
    error: 'Save failed',
  })
  await apiCall().catch(() => {}) // user already saw the toast; don't double-report
}

// GOOD (option B) — single consumer; await drives both UX and error handling
async function handleSubmit() {
  const promise = apiCall()
  toast.promise(promise, { loading: 'Saving...', success: 'Saved', error: 'Save failed' })
  try {
    await promise
  } catch {
    // already surfaced by toast; swallow to avoid Sentry noise
  }
}
```

**Detection:**

```bash
rg "toast\.promise" --type ts --type tsx -A 8 | rg "await" | rg -v "catch|try"
```

**Rule:** `toast.promise()` and a bare `await` of the same promise are two independent consumers. If the toast handles the error UI, add `.catch(() => {})` to the await (or drop the await) to prevent the rejection from being re-surfaced as Sentry noise for an error the user has already seen.

---

## D. Recommended Pattern

### D1. `hook-decomposition-complex-component` — Focused Hooks

Decompose complex components into focused hooks with typed interfaces.

```tsx
// Before: 300-line component doing everything
function ReportForm() { /* state + validation + submit + persistence */ }

// After: focused hooks
function useReportState(initial?: Report) { /* state management */ }
function useReportValidation(values: Report) { /* field validation */ }
function useReportSubmit() { /* API calls + error handling */ }

function ReportForm({ initial }: { initial?: Report }) {
  const { values, setField } = useReportState(initial)
  const errors = useReportValidation(values)
  const { submit, isSubmitting } = useReportSubmit()
  // Component is now ~50 lines of clean JSX
}
```

Attribution

darellchua2darellchua2
View sourceMore from darellchua2 →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →