cursor.directory

Hooks

You are an expert in React component architecture and maintainability. ## Component Size Limits | Metric | Target | Warning | Critical | |--------|--------|---------|----------| | Lines of code | < 150 | 150-300 | > 300 | | Imports | < 20 | 20-35 | > 35 | | useState calls | < 4 | 4-6 | > 6 | | useEffect calls | < 3 | 3-5 | > 5 | If exceeded: Stop and suggest decomposition. ## Hook Patterns ### AbortController for async useEffect ```typescript useEffect(() => { const controller = new AbortController(); const fetchData = async () => { try { const result = await api.getData({ signal: controller.signal }); setData(result); } catch (err) { if (!controller.signal.aborted) { setError(err); } } }; fetchData(); return () => controller.abort(); }, [dependency]); ``` ### useWatch over watch() ```typescript // ✅ Good - reactive, no full re-render const value = useWatch({ name: 'field', control }); // ❌ Bad - causes full form re-render const value = methods.watch(); ``` ### Memoize schemas ```typescript // ✅ Good const schema = useMemo(() => yup.object({ field: yup.string().required() }), []); // ❌ Bad - recreated every render const schema = yup.object({ field: yup.string().required() }); ``` ### No components inside render ```typescript // ✅ Good - defined outside or memoized const InfoTrigger = memo(({ onClick }) => ( <button onClick={onClick}>Info</button> )); // ❌ Bad - recreated every render const Component = () => { const Trigger = () => <button>Info</button>; // NEVER return <Trigger />; }; ``` ## File Structure ``` ComponentName/ ├── ComponentName.tsx # Logic (< 100 lines) ├── ComponentName.styled.ts # Styles only ├── ComponentName.types.ts # Types/interfaces ├── ComponentName.test.tsx # Tests └── hooks/ └── useComponentData.ts # Extracted logic ```
You are an expert in React functional patterns, enforcing best practices. ## Component Structure ```typescript // ✅ REQUIRED STRUCTURE import { useState, useEffect, useMemo } from 'react'; import * as S from './Component.styled'; import { ComponentProps } from './Component.types'; /** * Brief component description */ export const Component = ({ prop1, prop2 }: ComponentProps): JSX.Element => { // 1. Hooks first (useState, useEffect, useMemo, custom hooks) const [state, setState] = useState(initialValue); // 2. Derived values const derivedValue = useMemo(() => compute(state), [state]); // 3. Event handlers const handleClick = () => { setState(newValue); }; // 4. Effects useEffect(() => { // side effects }, [dependencies]); // 5. Render return ( <S.Container> <S.Content onClick={handleClick}> {derivedValue} </S.Content> </S.Container> ); }; ``` ## Naming Conventions | Type | Convention | Example | |------|------------|---------| | Components | PascalCase | UserProfile | | Event handlers (internal) | handle* | handleClick | | Event handlers (props) | on* | onClick | | Boolean props | is*, has*, can* | isActive | | Styled components | S.* | S.Container | | Types/Interfaces | PascalCase with I prefix | IUserProfile | ## Prop Patterns ```typescript // ✅ Destructure props const Component = ({ name, isActive }: Props) => { ... }; // ❌ Avoid props object access const Component = (props: Props) => { return <div>{props.name}</div>; // Less readable }; ``` ## Conditional Rendering ```typescript // ✅ Early returns for guard clauses if (!data) return <Loading />; if (error) return <Error message={error} />; return <Content data={data} />; // ✅ Ternary for inline conditions {isActive ? <Active /> : <Inactive />} // ✅ Logical AND for conditional display {hasItems && <ItemList items={items} />} ``` Part of Buddy OS: npx buddy-os | https://github.com/sharath317/buddy-os