diff --git a/.gitignore b/.gitignore index 8996fca1..5a19a7f0 100644 --- a/.gitignore +++ b/.gitignore @@ -163,4 +163,7 @@ yarn-error.log* .playwright-mcp # YoYo AI version control directory -.yoyo/ \ No newline at end of file +.yoyo/ + +.specstory/ +.cursorindexingignore \ No newline at end of file diff --git a/docs/react-guide.md b/docs/react-guide.md new file mode 100644 index 00000000..957de419 --- /dev/null +++ b/docs/react-guide.md @@ -0,0 +1,813 @@ +# React guide + +A lot of AI-generated React code is heavily biased toward using useState and useEffect, often in verbose or naive ways. That’s because these hooks are: + +1. Explicit, making them easy for LLMs to pattern-match. +2. Safe defaults, requiring less architectural decision-making. +3. Well-documented, so the model’s training data is oversaturated with examples. + +But modern, well-architected React apps rarely rely on raw useState/useEffect beyond simple UI state or lifecycle quirks. Here’s a breakdown of what serious devs use instead—and why this is the real moat for human engineers. + +Alternatives to useState and useEffect + +1. State Machines / Statecharts (e.g. Zustand) +- Why: Replaces ad-hoc local state management with deterministic, declarative logic. +- Advantage: Less bugs, easier to test, clearer state transitions. +- AI gap: LLMs struggle to model state transitions declaratively. LLM Agent must overcome this. + +2. React Query / SWR for Data Fetching +- Why: Avoids useEffect-based data fetching and loading/error state juggling. +- Advantage: Caching, stale-while-revalidate, retries, pagination—all handled out of the box. +- AI gap: AI often uses useEffect + fetch and forgets cleanup, race conditions, or caching. + +3. Global State with Zustand, Jotai, Recoil, or Redux Toolkit +- Why: Local useState doesn’t scale for shared or persistent state. +- Advantage: Better performance, devtools support, persistence. +- AI gap: Defaults to prop-drilling or bloated context APIs. + +4. Custom Hooks and Composition +- Why: Reuse logic cleanly without bloating components or copy-pasting useEffect. +- Advantage: Separation of concerns, encapsulation. +- AI gap: Often fails to factor logic out, keeping everything in one component. + +## Why This Matters + +AI can mimic patterns, but it can’t: +- Architect a system for long-term maintainability. +- Predict runtime performance tradeoffs. +- Refactor spaghetti effects into custom hooks or state machines. +- Decide when state should be colocated vs globalized. +- Avoid footguns like stale closures, unnecessary re-renders, or race conditions. + +In short: AI is great at writing code, but bad at engineering software. + +Takeaway +- Use fewer useEffect and useState hooks. +- Learn to design systems, not just code components. +- Reach for declarative, composable patterns. +- Understand caching, reactivity, and scalability beyond local state. + +Here’s a side-by-side breakdown of common AI-generated React patterns (bad) vs what experienced engineers write (good) across 4 key areas: state management, data fetching, side effects, and logic reuse. + +## 1. State Management + +❌ Bad (AI-style useState) + +```typescript +function TodoApp() { + const [todos, setTodos] = useState([]); + const [input, setInput] = useState(''); + + const addTodo = () => { + setTodos([...todos, { text: input }]); + setInput(''); + }; + + return ( + <> + setInput(e.target.value)} /> + +
{item.title}
)} +{item.title}
)} +