Cursor rule
.cursor/rules/20-hermes-app-hooks.mdcCursor rules
Quality
73/100
Scores the file, not the repository.Length
780 words
20 headings · 11 code blocksRepository
45
— · pushed 3 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Hermes App - Custom Hooks Rules89## Hook Naming1011- **MUST** start with `use` prefix: `useDownloadActions`, `useTheme`, `useApiKeys`12- File name must match hook name: `useTheme.ts` exports `useTheme`13- Export as named export, not default14- Use descriptive names that indicate purpose1516## Hook Structure1718### Basic Template19```typescript20interface UseFeatureOptions {21 initialValue?: string;22 onSuccess?: (data: Data) => void;23}2425interface UseFeatureReturn {26 data: Data | null;27 isLoading: boolean;28 error: Error | null;29 actions: {30 fetch: () => Promise<void>;31 reset: () => void;32 };33}3435/**36 * @example37 * const { data, isLoading, actions } = useFeature({ initialValue: 'test' });38 */39export function useFeature(options: UseFeatureOptions = {}): UseFeatureReturn {40 const [data, setData] = useState<Data | null>(null);41 const [isLoading, setIsLoading] = useState(false);42 const [error, setError] = useState<Error | null>(null);4344 return { data, isLoading, error, actions: { fetch, reset } };45}46```4748### TypeScript Requirements49- Define explicit return types50- Create interfaces for options and return values51- Avoid `any` type5253## Hook Patterns5455### Data Fetching (TanStack Query)56```typescript57export function useApiKeys() {58 const queryClient = useQueryClient();5960 const { data: apiKeys, isLoading, error } = useQuery({61 queryKey: ["apiKeys"],62 queryFn: fetchApiKeys,63 });6465 const createMutation = useMutation({66 mutationFn: createApiKey,67 onSuccess: () => {68 queryClient.invalidateQueries({ queryKey: ["apiKeys"] });69 },70 });7172 return {73 apiKeys: apiKeys ?? [],74 isLoading,75 error,76 createApiKey: createMutation.mutateAsync,77 isCreating: createMutation.isPending,78 };79}80```8182### Action Hook83```typescript84export function useDownloadActions() {85 const queryClient = useQueryClient();8687 const pauseMutation = useMutation({88 mutationFn: pauseDownload,89 onSuccess: () => {90 queryClient.invalidateQueries({ queryKey: ["downloads"] });91 toast.success("Download paused");92 },93 onError: (error) => toast.error(`Failed: ${error.message}`),94 });9596 const pause = useCallback(97 (id: string) => pauseMutation.mutateAsync(id),98 [pauseMutation]99 );100101 return { pause, isPausing: pauseMutation.isPending };102}103```104105### Utility Hook106```typescript107export function useDebounce<T>(value: T, delay: number = 500): T {108 const [debouncedValue, setDebouncedValue] = useState<T>(value);109110 useEffect(() => {111 const handler = setTimeout(() => setDebouncedValue(value), delay);112 return () => clearTimeout(handler);113 }, [value, delay]);114115 return debouncedValue;116}117```118119### Context Hook120```typescript121export function useAuth() {122 const context = useContext(AuthContext);123 if (!context) {124 throw new Error("useAuth must be used within AuthProvider");125 }126 return context;127}128```129130## TanStack Query Integration131132### Query Keys133Use hierarchical, descriptive keys with parameters:134135```typescript136// ✅ Good137queryKey: ["downloads", "list", { status: "active" }]138queryKey: ["downloads", "detail", downloadId]139140// ❌ Bad141queryKey: ["data"]142queryKey: ["downloads"] // Too generic143```144145### Mutations146- Invalidate queries after successful mutations147- Handle optimistic updates when appropriate148- Show toast notifications for user feedback149150```typescript151const mutation = useMutation({152 mutationFn: updateItem,153 onMutate: async (newItem) => {154 await queryClient.cancelQueries({ queryKey: ["items"] });155 const prev = queryClient.getQueryData(["items"]);156 queryClient.setQueryData(["items"], (old: Item[]) =>157 old.map((item) => (item.id === newItem.id ? newItem : item))158 );159 return { prev };160 },161 onError: (err, newItem, context) => {162 queryClient.setQueryData(["items"], context?.prev);163 toast.error("Update failed");164 },165 onSettled: () => {166 queryClient.invalidateQueries({ queryKey: ["items"] });167 },168});169```170171## Hook Composition172173Compose smaller hooks into larger ones:174175```typescript176export function useQueueData() {177 const { downloads, isLoading: isLoadingDownloads } = useDownloads();178 const { stats, isLoading: isLoadingStats } = useStats();179 const { pause, resume, cancel } = useDownloadActions();180 const filters = useFilters();181182 const filteredDownloads = useMemo(183 () => applyFilters(downloads, filters.active),184 [downloads, filters.active]185 );186187 return {188 downloads: filteredDownloads,189 stats,190 isLoading: isLoadingDownloads || isLoadingStats,191 actions: { pause, resume, cancel },192 filters,193 };194}195```196197## Side Effects198199### Dependencies200- Always declare all dependencies201- Use ESLint to catch missing dependencies202- Extract stable references with `useCallback`203204### Cleanup205- Return cleanup function from effects206- Cancel pending requests207- Clear timers and subscriptions208209```typescript210useEffect(() => {211 const controller = new AbortController();212213 async function fetchData() {214 try {215 const data = await fetch(url, { signal: controller.signal });216 setData(data);217 } catch (error) {218 if (error.name !== "AbortError") setError(error);219 }220 }221222 fetchData();223 return () => controller.abort();224}, [url]);225```226227## Performance228229### Memoization230- Use `useCallback` for function references passed as props231- Use `useMemo` for expensive computations232- Don't over-optimize - profile first233234```typescript235export function useFilters() {236 const [filters, setFilters] = useState<Filter[]>([]);237238 const activeFilters = useMemo(239 () => filters.filter((f) => f.active),240 [filters]241 );242243 const addFilter = useCallback((filter: Filter) => {244 setFilters((prev) => [...prev, filter]);245 }, []);246247 return { filters, activeFilters, addFilter };248}249```250251## Error Handling252253```typescript254export function useVideoInfo(url: string) {255 const [info, setInfo] = useState<VideoInfo | null>(null);256 const [error, setError] = useState<Error | null>(null);257 const [isLoading, setIsLoading] = useState(false);258259 const fetchInfo = useCallback(async () => {260 if (!url) return;261262 setIsLoading(true);263 setError(null);264265 try {266 const data = await api.getVideoInfo(url);267 setInfo(data);268 } catch (err) {269 const error = err instanceof Error ? err : new Error("Unknown error");270 setError(error);271 toast.error(`Failed: ${error.message}`);272 } finally {273 setIsLoading(false);274 }275 }, [url]);276277 useEffect(() => {278 fetchInfo();279 }, [fetchInfo]);280281 return { info, error, isLoading, refetch: fetchInfo };282}283```284285
Also in TechSquidTV/Hermes
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 |
|---|---|---|---|---|---|
| TechSquidTV/Hermes.cursor/rules/00-project.mdc · 45 | Cursor rules | setuplint-formatstylearch+4 | 89/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-app.mdc · 45 | Cursor rules | lint-formatstylearchtypes+5 | 88/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-api.mdc · 45 | Cursor rules | stylearchdependenciesapi+2 | 77/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-db.mdc · 45 | Cursor rules | teststylearchtesting-strategy+3 | 73/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-components.mdc · 45 | Cursor rules | archtypesuiperformance+1 | 65/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-routes.mdc · 45 | Cursor rules | archapiuido-not | 65/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-docker.mdc · 45 | Cursor rules | setupbuildstylesecurity+4 | 84/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-docs.mdc · 45 | Cursor rules | setuplint-formatstylearch+3 | 81/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-tests.mdc · 45 | Cursor rules | buildteststylearch+4 | 85/100 | 3 days ago |
Diff against .cursor/rules/00-project.mdc Diff against .cursor/rules/10-hermes-api.mdc Diff against .cursor/rules/10-hermes-app.mdc Diff against .cursor/rules/20-hermes-api-api.mdc Diff against .cursor/rules/20-hermes-api-db.mdc Diff against .cursor/rules/20-hermes-api-tests.mdc Diff against .cursor/rules/20-hermes-app-components.mdc Diff against .cursor/rules/20-hermes-app-routes.mdc Diff against .cursor/rules/30-docker.mdc Diff against .cursor/rules/30-docs.mdc Diff against .cursor/rules/30-tests.mdc
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 | |
| 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 | |
| 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 | |
| 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 |
