Cursor rule
.cursor/rules/react-patterns.mdcReact webapp conventions (Math Tutoring App)
Cursor rules
Quality
69/100
Scores the file, not the repository.Length
712 words
6 headings · 2 code blocksRepository
0
— · pushed 112 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# React Patterns — `webapp/`89Stack: **React 19**, **Vite**, **React Router**, **Firebase** (client SDK).1011## useEffect rules (read first)1213- **Computed values → plain vars, not state.** Never use `useEffect` to sync props into state. Derive inline.14- **Router-driven resets → `key`, not `useEffect`.** When a screen must reinitialize from a route param (or equivalent identity), let the parent set `key={…}` to remount instead of mirroring props into state in an effect.15- **Callbacks in deps → `useEffectEvent`.** Never put a parent-supplied callback directly in a `useEffect` dependency array — it may not be stable. Wrap it with `useEffectEvent`. Prefer `useEffectEvent` over `useCallback` for this.16- **“Latest value” reads → `useEffectEvent`.** If you need a value inside an effect but don’t want it to re-trigger the effect, read it inside a `useEffectEvent` wrapper.17- **Fetching / async load → custom hooks only.** Never perform data loading (Firestore queries, `fetch` to the Express API, etc.) directly inside `useEffect` in a route or feature component. Use a `useXxx` hook in `src/hooks/` that encapsulates loading (e.g. TanStack Query, or a dedicated hook that wraps your service calls). Components call hooks; they don’t inline async load effects.18- **Expensive calculations → `useMemo`.** Don’t compute inside an effect and write to state unless it’s truly an external sync; derive or memoize.1920**Exceptions:** Long-lived **subscriptions** (e.g. Firebase `onAuthStateChanged` in `AuthProvider`) belong in an effect with a proper cleanup return — that is not “fetching”; keep listeners from leaking.2122```tsx23// Rules 4 & 3 — useEffectEvent for latest values and unstable callbacks24import { useEffect, useEffectEvent, useState } from 'react';2526function TypingIndicator({ conversationId, onTick }: Props) {27 const [dots, setDots] = useState(1);2829 const fireTick = useEffectEvent(() => {30 setDots((d) => (d % 3) + 1);31 onTick(); // parent callback — not listed in useEffect deps32 });3334 useEffect(() => {35 const id = setInterval(fireTick, 500);36 return () => clearInterval(id);37 }, [conversationId]); // only remount interval when conversation changes38}3940// Rules 2 & 1 — key for remount; derived strings, not effect-synced state41// Parent (e.g. route wrapper)42<Chat key={conversationId ?? 'new'} />4344// Inside Chat — no useEffect to copy props into state45function Chat({ conversationId }: { conversationId: string | null }) {46 const { data } = useConversationHistory(); // Rule 5 — load via hook47 const title = conversationId ? `Thread ${conversationId.slice(0, 8)}` : 'New chat'; // Rule 148 const sortedMessages = useMemo(49 () => [...(data?.messages ?? [])].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()),50 [data?.messages]51 ); // Rule 652}53```5455## Custom hooks per resource5657- Each **distinct data domain** gets its own hook in `src/hooks/`, named after that domain (`useAuth`, `useConversationHistory`, etc.).58- **One hook per resource shape** where it helps clarity: e.g. conversation list/history vs. auth session vs. a single upload flow — don’t stuff unrelated async into one mega-hook.59- **Services stay in `src/services/`** (`chatService`, `api`, `storageService`); hooks orchestrate those (and caching libraries if you add them). Screen components should not import `chatService` / call `fetch` directly for load paths that belong in a hook.6061```ts62// ✅ GOOD — names reflect resources / concerns63useAuth();64useConversationHistory();6566// ❌ BAD — Chat.tsx importing loadConversationHistory and wiring useEffect inline for the main load path67```6869## Layout7071| Area | Path |72|------|------|73| App shell & routes | `webapp/src/App.tsx`, `webapp/src/main.tsx` |74| Screens / features | `webapp/src/components/` (e.g. `Chat.tsx`, `Login.tsx`, `SignUp.tsx`) |75| Design system | `webapp/src/components/design-system/` |76| Hooks | `webapp/src/hooks/` |77| Context | `webapp/src/contexts/` |78| Client API / streaming | `webapp/src/services/api.ts` |79| Firestore / persistence | `webapp/src/services/chatService.ts`, `storageService.ts` |80| Global styles / tokens | `webapp/src/styles/tokens.ts` |8182There is no `src/pages/` tree; route-level views live under `src/components/`.8384## Data & side effects8586- **Firestore reads/writes** run in the **webapp** via the Firebase client SDK — not from the Express API for normal chat persistence.87- **LLM / OpenAI** runs only **server-side** (`api/`). The webapp uses Express for chat streaming (`api.ts` / SSE) and sends **Firebase ID tokens**; never embed provider API keys in the client.88- Prefer **custom hooks** (`useAuth`, resource hooks above) for shared data and UI coordination.8990## Components9192- Prefer **focused components** under `src/components/` over single huge files; shared primitives live in `design-system/`.93- Keep **accessibility** in mind for interactive controls (buttons, forms, chat input).94
Also in VictorGoic0/Math-Tutoring-App
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| VictorGoic0/Math-Tutoring-App.cursor/rules/api-patterns.mdc · 0 | Cursor rules | setupstylearchsecurity+2 | 73/100 | 3 days ago | |
| VictorGoic0/Math-Tutoring-App.cursor/rules/linting.mdc · 0 | Cursor rules | lint-formatstyledo-notagent-behaviour | 68/100 | 3 days ago | |
| VictorGoic0/Math-Tutoring-App.cursor/rules/one-pr-at-a-time.mdc · 0 | Cursor rules | git | 16/100 | 3 days ago | |
| VictorGoic0/Math-Tutoring-App.cursor/rules/react-readability.mdc · 0 | Cursor rules | styleuido-not | 57/100 | 3 days ago |
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
