# Best Practices & Code Standards ## React Hooks - CRITICAL Rules ### ✅ ALWAYS Use Functional Updates with useState/useKV ```typescript // ❌ WRONG - Creates stale closure bugs const [items, setItems] = useState([]) const addItem = (newItem) => { setItems([...items, newItem]) // items is STALE! } // ✅ CORRECT - Always current const [items, setItems] = useState([]) const addItem = (newItem) => { setItems(currentItems => [...currentItems, newItem]) } ``` ### ✅ NEVER Reference State Outside Setter ```typescript // ❌ WRONG - Reading from closure const handleApprove = (id) => { setTimesheets(current => current.map(...)) const timesheet = timesheets.find(t => t.id === id) // STALE! } // ✅ CORRECT - Get from updated state const handleApprove = (id) => { setTimesheets(current => { const updated = current.map(...) const timesheet = updated.find(t => t.id === id) // CURRENT! return updated }) } ``` ### ✅ Use useCallback for Expensive Functions ```typescript // ✅ Prevents recreation on every render const handleSubmit = useCallback((data) => { // expensive operation }, [dependencies]) ``` ### ✅ Use useMemo for Expensive Calculations ```typescript // ✅ Only recalculates when dependencies change const expensiveValue = useMemo(() => { return data.reduce((acc, item) => acc + item.value, 0) }, [data]) ``` ## Data Persistence ### Use useKV for Persistent Data ```typescript // ✅ Data survives page refresh const [todos, setTodos] = useKV('user-todos', []) // ✅ ALWAYS use functional updates setTodos(current => [...current, newTodo]) ``` ### Use useState for Temporary Data ```typescript // ✅ UI state that doesn't need to persist const [isOpen, setIsOpen] = useState(false) const [searchQuery, setSearchQuery] = useState('') ``` ## TypeScript Best Practices ### Use Explicit Types ```typescript // ✅ Type all function parameters function processInvoice(invoice: Invoice): ProcessedInvoice { // ... } // ❌ Avoid any function process(data: any) { } // ✅ Use proper types function process(data: InvoiceData) { } ``` ### Use Type Guards ```typescript // ✅ Type narrowing function isInvoice(obj: unknown): obj is Invoice { return typeof obj === 'object' && obj !== null && 'invoiceNumber' in obj } ``` ### Use Const Assertions ```typescript // ✅ Literal types const status = 'approved' as const setTimesheet({ ...timesheet, status }) ``` ## Error Handling ### Wrap Async Operations ```typescript // ✅ Handle errors gracefully const loadData = async () => { try { const data = await fetchData() setData(data) } catch (error) { console.error('Failed to load data:', error) toast.error('Failed to load data') setError(error) } } ``` ### Use Error Boundaries ```typescript // ✅ Catch render errors ``` ## Performance Optimization ### Avoid Inline Functions in JSX ```typescript // ❌ Creates new function every render // ✅ Use useCallback or bind const handleButtonClick = useCallback(() => handleClick(id), [id]) ``` ### Memo Expensive Components ```typescript // ✅ Prevents unnecessary re-renders const ExpensiveComponent = memo(({ data }) => { // expensive rendering }, (prevProps, nextProps) => { return prevProps.data === nextProps.data }) ``` ### Virtual Scrolling for Large Lists ```typescript // ✅ For lists with 100+ items import { useVirtualScroll } from '@/hooks/use-virtual-scroll' const VirtualList = ({ items }) => { const { visibleItems, containerProps, scrollProps } = useVirtualScroll(items) return (
{visibleItems.map(item => )}
) } ``` ## Component Organization ### Keep Components Under 250 Lines ```typescript // ✅ Extract complex logic to custom hooks function MyComponent() { const { data, actions } = useMyComponentLogic() return
{/* simple JSX */}
} function useMyComponentLogic() { // complex state management return { data, actions } } ``` ### Single Responsibility ```typescript // ✅ Each component does one thing const UserAvatar = ({ user }) => const UserName = ({ user }) => {user.name} const UserBadge = ({ user }) => {user.role} // Compose them const UserCard = ({ user }) => ( ) ``` ## Styling ### Use Tailwind Composition ```typescript // ✅ Compose utilities
``` ### Extract Common Patterns ```typescript // ✅ Consistent spacing const cardClasses = "p-6 bg-card rounded-lg border" const flexRowClasses = "flex items-center gap-4" ``` ### Use cn() for Conditional Classes ```typescript import { cn } from '@/lib/utils' // ✅ Merge classes safely
``` ## State Management with Redux ### Use Typed Hooks ```typescript // ✅ Type-safe hooks import { useAppSelector, useAppDispatch } from '@/store/hooks' const user = useAppSelector(state => state.auth.user) const dispatch = useAppDispatch() ``` ### Keep Slices Focused ```typescript // ✅ One slice per domain const timesheetsSlice = createSlice({ name: 'timesheets', // only timesheet state }) ``` ### Use Redux for Global State ```typescript // ✅ App-wide state - Authentication - UI preferences (theme, locale) - Current view/navigation // ✅ Component state - Form inputs - Local UI state (modals, dropdowns) ``` ## Accessibility ### Semantic HTML ```typescript // ✅ Use semantic elements // ❌ Avoid div soup
Click me
// ✅ Use button ``` ### ARIA Labels ```typescript // ✅ Label interactive elements ``` ### Keyboard Navigation ```typescript // ✅ Support keyboard ``` ## Testing ### Test Business Logic ```typescript // ✅ Unit test hooks describe('useInvoiceCalculations', () => { it('calculates total correctly', () => { const { result } = renderHook(() => useInvoiceCalculations(data)) expect(result.current.total).toBe(1000) }) }) ``` ### Test User Interactions ```typescript // ✅ Integration tests it('approves timesheet on click', async () => { render() await userEvent.click(screen.getByText('Approve')) expect(screen.getByText('Approved')).toBeInTheDocument() }) ``` ## Security ### Sanitize User Input ```typescript // ✅ Validate and sanitize function handleSubmit(input: string) { const sanitized = input.trim().slice(0, 100) if (!sanitized) { toast.error('Input required') return } processInput(sanitized) } ``` ### Never Log Sensitive Data ```typescript // ❌ Don't log passwords, tokens console.log('User data:', userData) // ✅ Log safe data only console.log('User ID:', userId) ``` ### Use Permission Gates ```typescript // ✅ Check permissions import { PermissionGate } from '@/components/PermissionGate' ``` ## File Organization ``` src/ ├── components/ │ ├── ui/ # Reusable UI components │ ├── views/ # Page-level components │ └── [Feature].tsx # Feature components ├── hooks/ │ ├── use-[name].ts # Custom hooks │ └── index.ts # Barrel export ├── lib/ │ ├── types.ts # Type definitions │ ├── utils.ts # Utility functions │ └── constants.ts # App constants ├── store/ │ ├── slices/ # Redux slices │ ├── hooks.ts # Typed Redux hooks │ └── store.ts # Store configuration └── data/ └── *.json # Static data files ``` ## Git Commit Messages ```bash # ✅ Descriptive commits fix: resolve stale closure bug in timesheet approval feat: add error boundary to lazy-loaded views perf: memoize dashboard metrics calculation refactor: extract invoice logic to custom hook # ❌ Vague commits fix: bug update: changes wip ``` ## Documentation ### JSDoc for Complex Functions ```typescript /** * Calculates the gross margin for a given period * @param revenue - Total revenue in the period * @param costs - Total costs in the period * @returns Gross margin as a percentage (0-100) */ function calculateGrossMargin(revenue: number, costs: number): number { return revenue > 0 ? ((revenue - costs) / revenue) * 100 : 0 } ``` ### README for Complex Features ```markdown # Feature Name ## Purpose Brief description ## Usage Code examples ## API Function signatures ## Testing How to test ``` ## Common Pitfalls to Avoid 1. ❌ Stale closures in event handlers 2. ❌ Mutating state directly 3. ❌ Missing dependency arrays 4. ❌ Inline styles (use Tailwind) 5. ❌ Deeply nested components 6. ❌ Any types everywhere 7. ❌ No error handling 8. ❌ Blocking the UI thread 9. ❌ Memory leaks (uncleared timeouts/intervals) 10. ❌ Prop drilling (use context or Redux) ## Checklist for Pull Requests - [ ] No console.logs in production code - [ ] All new functions have types - [ ] Complex logic has comments - [ ] New features have error handling - [ ] useState uses functional updates - [ ] Expensive calculations are memoized - [ ] No accessibility regressions - [ ] Code follows existing patterns - [ ] No duplicate code - [ ] Cleaned up unused imports