๐Ÿ”งAutoSkills
All items
skillv1.0.0

react-modern

Modern React 18+ patterns: hooks, Suspense, Server Components, ref semantics

reactjavascripttypescriptfrontend

Install

$npx autoskills --items react-modern

Or scan + install everything matching your stack with npx autoskills.

Modern React

React 18+ is a different language than React 16. If you're applying hooks-era advice, you're behind. This skill covers the patterns that actually survived.

Hooks: what's real, what's cargo cult

  • useEffect is a synchronization primitive, not a "lifecycle". Reach for it only when you need to sync with an external system (DOM, subscription, network). Computing derived state from props belongs in render or useMemo, not effects.
  • No state-in-effect. useEffect(() => setX(deriveFromProps(p)), [p]) is always wrong. Compute x inline.
  • Refs are an escape hatch. If you find yourself reading .current to make rendering work, you're fighting React. Refs are for DOM nodes, mutable values that don't trigger re-render, and integrations with non-React code.
  • useMemo/useCallback are not free. Profile before adding. Reference-stability matters mainly for child memoization, not for hot loops.

Server Components (Next.js / RSC frameworks)

  • Default to Server Components. Mark 'use client' only on files that need state, effects, or browser APIs.
  • Don't pass functions across the server/client boundary. Functions can't serialize. Pass primitives, plain objects, or Server Actions.
  • Server Components can be async. async function Page() and await db.query(...) directly inside the component is the canonical pattern โ€” no useEffect + useState loaders.
  • Streaming is opt-in via Suspense. Wrap slow data with <Suspense fallback={...}> to send the rest of the tree immediately.

Suspense and data fetching

  • Suspense boundaries are render boundaries. Place them where you want a loading state, not at the data-fetch site.
  • use(promise) is the data hook for RSC and forthcoming use in client. Don't write useEffect + useState for fetches when a higher-level framework gives you Suspense-aware fetching.

State

  • Local state first, then context, then a library. Reaching for Zustand/Redux on day one is overkill.
  • Don't put server state in client state libraries. TanStack Query / RTK Query / SWR are for server state. Zustand is for UI state. Mixing them produces stale-cache bugs.

Forms

  • Uncontrolled by default. <form action={serverAction}> with FormData reads in the action. Controlled inputs only when you need real-time validation/derivation.
  • useActionState (formerly useFormState) is the canonical progress/error hook for Server Actions.

Rendering and keys

  • Keys are identity, not index. Using array index as key corrupts state when the list reorders.
  • List children with stable IDs always. If your data doesn't have IDs, generate them once at the source, not at render time.

What to delete from old codebases

  • React.FC โ€” adds implicit children, breaks generics. Use explicit prop interfaces.
  • componentDidMount/componentWillUnmount โ€” class lifecycle is over.
  • forwardRef wrapper โ€” React 19 accepts ref as a regular prop. Strip the wrapper.
  • Most useCallback/useMemo โ€” unless you measured them helping.

Quick checklist for new components

  1. Server Component by default โ€” add 'use client' only if needed.
  2. Async-await data inline (no effect-based fetching).
  3. State is local until proven otherwise.
  4. Stable keys for lists.
  5. Form actions over controlled inputs unless you need real-time state.
  6. Suspense boundaries where you want loading UI.