agentsclimarketplace

React expert

Skill Akayashuu/agent-skills/skills/react-expert

Use when writing, reviewing, or refactoring React components — taming re-renders, deciding what belongs in state vs derived during render, fixing useEffect overuse/race conditions, key/list bugs, useMemo/useCallback noise, context, refs, or server vs client component ("use client") boundaries.From its SKILL.md

Install
npx -y skills add Akayashuu/agent-skills --skill react-expert

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.3 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it

React Expert

Overview

Idiomatic React is derive, don't store; render, don't sequence. Most state is redundant and most effects are a smell — the UI is a pure function of props, state, and context, and useEffect exists only to sync with systems outside React. These are the judgment calls a linter can't make for you.

Quick Reference

GoalDoAvoid
Value computable from props/statecompute in renderuseState + useEffect to "keep it updated"
React to a prop/state changederive in render, or key to resetuseEffect watching that value
Talk to a non-React systemuseEffect (subscriptions, DOM, timers)effects for data transforms
List itemsstable id keykey={index}
Fetch dataframework / TanStack Query / RSChand-rolled useEffect + fetch
Avoid prop-drillingcomposition (children) then contextone giant global context
Expensive recompute / referential depuseMemo/useCallback with a real reasonmemoizing everything by default
Form inputcontrolled, or uncontrolled + refmixing both on one field
Read latest props/state inside an EffectuseEffectEvent (stable in 19.2)widening effect deps to silence the linter

Core Patterns

Derive during render — don't mirror props/state into an effect. The effect runs after paint, so the UI flashes a stale value, and the extra render is pure waste:

// ❌ redundant state + effect: extra render, stale frame, easy to desync
function Cart({ items }: { items: Item[] }) {
  const [total, setTotal] = useState(0)
  useEffect(() => { setTotal(items.reduce((s, i) => s + i.price, 0)) }, [items])
  return <p>{total}</p>
}
// ✅ it's just a value; compute it. Wrap in useMemo only if profiling says so.
const total = items.reduce((s, i) => s + i.price, 0)

Runnable: examples/derive-during-render.tsx

Reset state on identity change with key, not an effect. Changing the key remounts the subtree, discarding old state — no manual resync:

// ❌ effect to "reset form when user changes" — stale between switch and effect
useEffect(() => { setDraft('') }, [userId])
// ✅ a different user is a different form; let React remount it
<ProfileForm key={userId} />

Runnable: examples/reset-with-key.tsx

Effects are for external systems — and must clean up to dodge races. Anything async needs an ignore flag or AbortController, which is exactly why a data library is better:

// ❌ no cleanup: a slow earlier request resolves last and overwrites the new one
useEffect(() => { fetch(`/api/u/${id}`).then(r => r.json()).then(setUser) }, [id])
// ✅ ignore stale resolutions (or just use TanStack Query / an RSC loader)

Runnable: examples/abortable-effect-fetch.tsx

Composition kills prop-drilling before context does. Pass rendered UI as children so intermediate components never see props they only forward:

// ❌ Layout threads `user` through just to hand it down
<Layout user={user} />  // Layout forwards user → Sidebar → Avatar
// ✅ render the leaf where the data lives; Layout stays agnostic
<Layout sidebar={<Avatar user={user} />} />

Separate non-reactive logic with useEffectEvent — don't lie to the deps array. Stable since React 19.2: an Effect Event always sees the latest props/state but isn't reactive, so it stays out of the deps array — for fresh values an Effect shouldn't re-synchronize on:

const onConnected = useEffectEvent(() => log('connected', theme)) // reads latest theme
useEffect(() => {
  const conn = createConnection(roomId)
  conn.on('connected', () => onConnected())
  conn.connect()
  return () => conn.disconnect()
}, [roomId]) // re-runs on roomId only; theme change does NOT reconnect

Runnable: examples/use-effect-event.tsx

Memoize for referential stability, not "speed." A useCallback/useMemo matters when its result is a dependency of a memoized child or another hook — otherwise it's noise that adds its own cost:

const onSelect = useCallback((id: string) => dispatch(select(id)), [dispatch]) // stable prop for memo'd <Row>

React Compiler (1.0, stable Oct 2025) changes this calculus. It auto-memoizes at build time, so with the compiler on you can delete most hand-written useMemo/useCallback/React.memo. Keep them only where it bails out (components that break the Rules of React). The compiler optimizes; it won't fix stored derived state or over-broad effect deps.

Common Mistakes

  • useEffect to compute derived data — transform in render; effects are for syncing with the outside world.
  • key={index} — on reorder/insert, React reuses the wrong DOM/state. Use a stable id.
  • Stale closures — a callback captures the render's values; reading "latest" inside an Effect is what useEffectEvent is for — not a lie to the deps array.
  • Over-broad or missing deps — don't silence react-hooks/exhaustive-deps; fix the design (move logic out, use a ref, or a functional setState(prev => …)).
  • setState during render without a guard → infinite loop. Derive instead.
  • Premature memo/useMemo everywhere — measure first; memoization isn't free.
  • One giant context — every consumer re-renders on any field change. Split by concern (or by state vs dispatch).
  • ref for things render should own — refs are an imperative escape hatch (focus, measure, integrate non-React libs), not a place to stash render data.
  • Reading data in a Server Component then marking the whole tree 'use client' — keep 'use client' at the interactive leaf; fetch on the server.

When NOT to over-engineer

Local, cheap, self-contained UI state (a toggle, an input) needs no reducer, no context, no memo. Reach for useReducer, context splitting, or a data library when state is shared, complex, or async — not preemptively. In React 19 (stable), prefer the platform: use to unwrap promises/context, <form> Actions + useActionState for submission and pending UI, useOptimistic for optimistic updates, ref-as-prop (no more forwardRef), and Server Components to fetch on the server so the client ships less and avoids effect-based fetching entirely. Don't hand-roll what the framework now does.

See examples/ for self-contained, compiling versions of each pattern above (and patterns.tsx for the same three combined in one module).

Sources

What ships with it: 5 files

7.0 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.