# Common Patterns Frequently used patterns for forms, authentication, DataGrid, dialogs, and other common UI elements. --- ## Authentication with useAuth ### Getting Current User ```typescript import { useAuth } from '@/hooks/useAuth'; export const MyComponent: React.FC = () => { const { user } = useAuth(); // Available properties: // - user.id: string // - user.email: string // - user.username: string // - user.roles: string[] return (

Logged in as: {user.email}

Username: {user.username}

Roles: {user.roles.join(', ')}

); }; ``` **NEVER make direct API calls for auth** - always use `useAuth` hook. --- ## Forms with React Hook Form ### Basic Form ```typescript import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { TextField, Button } from '@mui/material'; import { useMuiSnackbar } from '@/hooks/useMuiSnackbar'; // Zod schema for validation const formSchema = z.object({ username: z.string().min(3, 'Username must be at least 3 characters'), email: z.string().email('Invalid email address'), age: z.number().min(18, 'Must be 18 or older'), }); type FormData = z.infer; export const MyForm: React.FC = () => { const { showSuccess, showError } = useMuiSnackbar(); const { register, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(formSchema), defaultValues: { username: '', email: '', age: 18, }, }); const onSubmit = async (data: FormData) => { try { await api.submitForm(data); showSuccess('Form submitted successfully'); } catch (error) { showError('Failed to submit form'); } }; return (
); }; ``` --- ## Dialog Component Pattern ### Standard Dialog Structure From BEST_PRACTICES.md - All dialogs should have: - Icon in title - Close button (X) - Action buttons at bottom ```typescript import { Dialog, DialogTitle, DialogContent, DialogActions, Button, IconButton } from '@mui/material'; import { Close, Info } from '@mui/icons-material'; interface MyDialogProps { open: boolean; onClose: () => void; onConfirm: () => void; } export const MyDialog: React.FC = ({ open, onClose, onConfirm }) => { return ( Dialog Title {/* Content here */} ); }; ``` --- ## DataGrid Wrapper Pattern ### Wrapper Component Contract From BEST_PRACTICES.md - DataGrid wrappers should accept: **Required Props:** - `rows`: Data array - `columns`: Column definitions - Loading/error states **Optional Props:** - Toolbar components - Custom actions - Initial state ```typescript import { DataGridPro } from '@mui/x-data-grid-pro'; import type { GridColDef } from '@mui/x-data-grid-pro'; interface DataGridWrapperProps { rows: any[]; columns: GridColDef[]; loading?: boolean; toolbar?: React.ReactNode; onRowClick?: (row: any) => void; } export const DataGridWrapper: React.FC = ({ rows, columns, loading = false, toolbar, onRowClick, }) => { return ( toolbar : undefined }} onRowClick={(params) => onRowClick?.(params.row)} // Standard configuration pagination pageSizeOptions={[25, 50, 100]} initialState={{ pagination: { paginationModel: { pageSize: 25 } }, }} /> ); }; ``` --- ## Mutation Patterns ### Update with Cache Invalidation ```typescript import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMuiSnackbar } from '@/hooks/useMuiSnackbar'; export const useUpdateEntity = () => { const queryClient = useQueryClient(); const { showSuccess, showError } = useMuiSnackbar(); return useMutation({ mutationFn: ({ id, data }: { id: number; data: any }) => api.updateEntity(id, data), onSuccess: (result, variables) => { // Invalidate affected queries queryClient.invalidateQueries({ queryKey: ['entity', variables.id] }); queryClient.invalidateQueries({ queryKey: ['entities'] }); showSuccess('Entity updated'); }, onError: () => { showError('Failed to update entity'); }, }); }; // Usage const updateEntity = useUpdateEntity(); const handleSave = () => { updateEntity.mutate({ id: 123, data: { name: 'New Name' } }); }; ``` --- ## State Management Patterns ### TanStack Query for Server State (PRIMARY) Use TanStack Query for **all server data**: - Fetching: useSuspenseQuery - Mutations: useMutation - Caching: Automatic - Synchronization: Built-in ```typescript // ✅ CORRECT - TanStack Query for server data const { data: users } = useSuspenseQuery({ queryKey: ['users'], queryFn: () => userApi.getUsers(), }); ``` ### useState for UI State Use `useState` for **local UI state only**: - Form inputs (uncontrolled) - Modal open/closed - Selected tab - Temporary UI flags ```typescript // ✅ CORRECT - useState for UI state const [modalOpen, setModalOpen] = useState(false); const [selectedTab, setSelectedTab] = useState(0); ``` ### Zustand for Global Client State (Minimal) Use Zustand only for **global client state**: - Theme preference - Sidebar collapsed state - User preferences (not from server) ```typescript import { create } from 'zustand'; interface AppState { sidebarOpen: boolean; toggleSidebar: () => void; } export const useAppState = create((set) => ({ sidebarOpen: true, toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })), })); ``` **Avoid prop drilling** - use context or Zustand instead. --- ## Summary **Common Patterns:** - ✅ useAuth hook for current user (id, email, roles, username) - ✅ React Hook Form + Zod for forms - ✅ Dialog with icon + close button - ✅ DataGrid wrapper contracts - ✅ Mutations with cache invalidation - ✅ TanStack Query for server state - ✅ useState for UI state - ✅ Zustand for global client state (minimal) **See Also:** - [data-fetching.md](data-fetching.md) - TanStack Query patterns - [component-patterns.md](component-patterns.md) - Component structure - [loading-and-error-states.md](loading-and-error-states.md) - Error handling