Files
community-rule/.cursor/rules/hooks.mdc
T
2026-04-18 14:12:49 -06:00

60 lines
1.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: Custom hooks live in app/hooks; co-locate logic, document via TSDoc.
globs: app/hooks/**/*.{ts,tsx}
alwaysApply: false
---
# Custom hooks
Reusable component logic lives in `app/hooks/`. Each hook is a small, focused
module with a TSDoc block that doubles as the API reference (no separate doc
file).
## File layout
- One file per hook: `app/hooks/use<Name>.ts`.
- Re-export from `app/hooks/index.ts`. Consumers import from the barrel:
`import { useFoo } from "../hooks";`.
- Companion unit test (when there is non-trivial logic): `tests/unit/hooks/`.
## Authoring rules
- Marked as a regular function (`export function useFoo() {}`); React handles
the `use*` naming convention.
- Wrap exposed callbacks in `useCallback` and computed values in `useMemo`
so consumers can list them in dependency arrays without churn.
- Read DOM/browser APIs only inside `useEffect` so the hook stays SSR-safe.
- Never throw on missing globals (e.g. `window`, `gtag`); guard and no-op.
## TSDoc — the only reference
Every exported hook gets a TSDoc block with:
- 12 sentence summary.
- `@param` per argument and `@returns` describing the shape.
- `@example` showing the typical call site.
```ts
/**
* Detect clicks outside a set of elements (e.g. close a dropdown).
*
* @param refs Elements that should NOT trigger the handler.
* @param handler Invoked when a click lands outside every ref.
* @param enabled Toggle without unmounting the consumer (default true).
*
* @example
* useClickOutside([menuRef, buttonRef], () => setOpen(false), open);
*/
export function useClickOutside(
refs: Array<RefObject<HTMLElement>>,
handler: (event: MouseEvent | TouchEvent) => void,
enabled = true,
): void { /* ... */ }
```
## Container/view consumption
Hooks belong in **container** files (per `component-structure.mdc`). Views
stay pure and read derived values via props — never call hooks that touch
state or side effects from a view.