--- name: code-patterns-practices description: React Native coding patterns, best practices, and common solutions for mobile development. Use when implementing features or refactoring code. allowed-tools: Read, Write, Edit --- # Code Patterns & Practices Common patterns and best practices for React Native development. ## When to Use - Implementing new features - Refactoring existing code - Choosing architecture patterns - Solving common problems - Improving code quality ## Component Patterns ### Custom Hooks ```typescript // Extract reusable logic function useToggle(initial = false) { const [value, setValue] = useState(initial); const toggle = useCallback(() => setValue(v => !v), []); return [value, toggle] as const; } // Usage const [isOpen, toggleOpen] = useToggle(); ``` ### Compound Components ```typescript // Create flexible component APIs interface TabsProps { children: React.ReactNode; defaultValue?: string; } function Tabs({ children, defaultValue }: TabsProps) { const [active, setActive] = useState(defaultValue); return ( {children} ); } Tabs.List = TabsList; Tabs.Trigger = TabsTrigger; Tabs.Content = TabsContent; // Usage Home Profile Home Content Profile Content ``` ### Render Props ```typescript // Share component logic interface DataLoaderProps { loadData: () => Promise; children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode; } function DataLoader({ loadData, children }: DataLoaderProps) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { loadData() .then(setData) .catch(setError) .finally(() => setLoading(false)); }, [loadData]); return <>{children(data, loading, error)}; } // Usage {(user, loading, error) => { if (loading) return ; if (error) return ; return ; }} ``` ## State Management Patterns ### Local State ```typescript // Keep it simple when possible function Counter() { const [count, setCount] = useState(0); return ; } ``` ### Shared State (Context) ```typescript // For cross-cutting concerns const ThemeContext = createContext(null!); export function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState<'light' | 'dark'>('light'); return ( {children} ); } export function useTheme() { const context = useContext(ThemeContext); if (!context) throw new Error('useTheme must be used within ThemeProvider'); return context; } ``` ### Global State (Zustand) ```typescript // For app-wide state import { create } from 'zustand'; interface UserState { user: User | null; setUser: (user: User) => void; logout: () => void; } export const useUserStore = create((set) => ({ user: null, setUser: (user) => set({ user }), logout: () => set({ user: null }), })); ``` ## Data Fetching Patterns ### With Async/Await ```typescript function useUserData(userId: string) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; async function fetchData() { try { const response = await fetch(`/api/users/${userId}`); const result = await response.json(); if (!cancelled) { setData(result); } } catch (err) { if (!cancelled) { setError(err); } } finally { if (!cancelled) { setLoading(false); } } } fetchData(); return () => { cancelled = true; }; }, [userId]); return { data, loading, error }; } ``` ## Performance Patterns ### Memoization ```typescript // Expensive calculations const expensiveValue = useMemo(() => { return calculateExpensiveValue(data); }, [data]); // Stable callbacks const handlePress = useCallback(() => { doSomething(value); }, [value]); // Component memoization const MemoizedChild = memo(function Child({ data }: ChildProps) { return {data}; }); ``` ### Lazy Loading ```typescript import { lazy, Suspense } from 'react'; const HeavyComponent = lazy(() => import('./HeavyComponent')); function App() { return ( }> ); } ``` ## Error Handling Patterns ### Error Boundaries ```typescript class ErrorBoundary extends React.Component< { children: React.ReactNode }, { hasError: boolean; error: Error | null } > { state = { hasError: false, error: null }; static getDerivedStateFromError(error: Error) { return { hasError: true, error }; } componentDidCatch(error: Error, info: React.ErrorInfo) { console.error('Error caught:', error, info); } render() { if (this.state.hasError) { return ; } return this.props.children; } } ``` ### Try-Catch Pattern ```typescript async function saveData() { try { await api.save(data); showSuccess('Saved!'); } catch (error) { if (error instanceof NetworkError) { showError('Network error. Check connection.'); } else if (error instanceof ValidationError) { showError(error.message); } else { showError('Something went wrong.'); } } } ``` ## Mobile-Specific Patterns ### Safe Area Handling ```typescript import { useSafeAreaInsets } from 'react-native-safe-area-context'; function Screen() { const insets = useSafeAreaInsets(); return ( {/* Content */} ); } ``` ### Keyboard Avoiding ```typescript import { KeyboardAvoidingView, Platform } from 'react-native'; {/* Input form */} ``` ## Best Practices 1. **Keep Components Small**: Single responsibility, easy to test 2. **Extract Custom Hooks**: Reuse logic across components 3. **Use TypeScript**: Catch errors early 4. **Handle Loading & Error States**: Better user experience 5. **Clean Up Side Effects**: Prevent memory leaks 6. **Optimize Wisely**: Profile before optimizing 7. **Test Behavior**: Focus on user interactions ## Anti-Patterns to Avoid - ❌ Massive components (>300 lines) - ❌ Prop drilling (use context/store instead) - ❌ Missing cleanup in useEffect - ❌ Inline function definitions in render - ❌ Mutating state directly - ❌ Over-optimization without measuring ## Resources - [React Patterns](https://reactpatterns.com/) - [React Native Best Practices](https://reactnative.dev/docs/performance)