React (Vite, client-side)
Sources: https://react.dev/learn/synchronizing-with-effects, https://vite.dev/guide/env-and-mode (fetched 2026-07-07 — re-verify if this content seems stale).
Scope note: this skill covers Vite+React client-side SPA patterns. If the project is Next.js (App Router, Server Components, Server Actions), this content does not apply — that's a meaningfully different rendering model needing its own guidance, not covered here.
Quick Reference
references/hooks-and-state.md — useEffect cleanup functions, stale-closure bugs (including the classic fetch-race-condition), dependency-array gotchas, when to reach for Context vs. a server-state library for API data.
references/vite-and-testing.md — Vite environment variables (VITE_ prefix, .env file precedence), and Vitest/React Testing Library basics.
Gotchas (do not skip)
- A
useEffect without a cleanup function leaks whatever it set up — subscriptions, intervals, event listeners, connections. If the setup has a natural "undo" (disconnect, unsubscribe, removeEventListener, clearInterval), the effect needs a cleanup function that calls it.
- React intentionally double-invokes effects in development (Strict Mode) — setup → cleanup → setup again. This is deliberately surfacing real cleanup bugs, not a framework bug. If an effect breaks under double-invocation, the cleanup is incomplete.
- Fetches inside
useEffect can race — a slow earlier request can resolve after a faster later one and overwrite it with stale data. Use an ignore flag set in the cleanup function to discard results from a superseded effect run (see references/hooks-and-state.md).
- Objects/functions created inline are a new reference every render — putting one directly in a dependency array causes the effect to re-run every render, not just when the "real" value changes. Move them outside the component or memoize them.
- Only
VITE_-prefixed environment variables reach client code. Anything without that prefix is silently undefined in import.meta.env — this is a deliberate secret-leakage guard, not a bug to work around.
Not Yet Covered Here (fetch before relying on these topics)
Next.js-specific rendering (Server Components, Server Actions, the App Router's data-fetching model) is explicitly out of scope for this skill — fetch https://nextjs.org/docs fresh rather than assuming Vite+React conventions transfer.
1---2name: react-code3description: Build client-side React apps with Vite — useEffect cleanup and stale-closure bugs, dependency-array gotchas, Context vs. server-state libraries for API data, Vite environment variables, and testing with Vitest. Scoped to Vite+React client-side apps — NOT Next.js (App Router/Server Components/Server Actions need different guidance). Triggers on "useEffect", "React hook", "stale closure", "dependency array", "Vite env variable", "React Vitest testing".4---56# React (Vite, client-side)78Sources: https://react.dev/learn/synchronizing-with-effects, https://vite.dev/guide/env-and-mode (fetched 2026-07-07 — re-verify if this content seems stale).910**Scope note:** this skill covers Vite+React client-side SPA patterns. If the project is Next.js (App Router, Server Components, Server Actions), this content does not apply — that's a meaningfully different rendering model needing its own guidance, not covered here.1112## Quick Reference1314- `references/hooks-and-state.md` — `useEffect` cleanup functions, stale-closure bugs (including the classic fetch-race-condition), dependency-array gotchas, when to reach for Context vs. a server-state library for API data.15- `references/vite-and-testing.md` — Vite environment variables (`VITE_` prefix, `.env` file precedence), and Vitest/React Testing Library basics.1617## Gotchas (do not skip)1819- **A `useEffect` without a cleanup function leaks whatever it set up** — subscriptions, intervals, event listeners, connections. If the setup has a natural "undo" (`disconnect`, `unsubscribe`, `removeEventListener`, `clearInterval`), the effect needs a cleanup function that calls it.20- **React intentionally double-invokes effects in development (Strict Mode)** — setup → cleanup → setup again. This is deliberately surfacing real cleanup bugs, not a framework bug. If an effect breaks under double-invocation, the cleanup is incomplete.21- **Fetches inside `useEffect` can race** — a slow earlier request can resolve after a faster later one and overwrite it with stale data. Use an `ignore` flag set in the cleanup function to discard results from a superseded effect run (see `references/hooks-and-state.md`).22- **Objects/functions created inline are a new reference every render** — putting one directly in a dependency array causes the effect to re-run every render, not just when the "real" value changes. Move them outside the component or memoize them.23- **Only `VITE_`-prefixed environment variables reach client code.** Anything without that prefix is silently `undefined` in `import.meta.env` — this is a deliberate secret-leakage guard, not a bug to work around.2425## Not Yet Covered Here (fetch before relying on these topics)2627Next.js-specific rendering (Server Components, Server Actions, the App Router's data-fetching model) is explicitly out of scope for this skill — fetch `https://nextjs.org/docs` fresh rather than assuming Vite+React conventions transfer.