| 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
// 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
// Create flexible component APIs
interface TabsProps {
children: React.ReactNode;
defaultValue?: string;
}
function Tabs({ children, defaultValue }: TabsProps) {
const [active, setActive] = useState(defaultValue);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
}
Tabs.List = TabsList;
Tabs.Trigger = TabsTrigger;
Tabs.Content = TabsContent;
// Usage
<Tabs defaultValue="home">
<Tabs.List>
<Tabs.Trigger value="home">Home</Tabs.Trigger>
<Tabs.Trigger value="profile">Profile</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="home">Home Content</Tabs.Content>
<Tabs.Content value="profile">Profile Content</Tabs.Content>
</Tabs>
Render Props
// Share component logic
interface DataLoaderProps<T> {
loadData: () => Promise<T>;
children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode;
}
function DataLoader<T>({ loadData, children }: DataLoaderProps<T>) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
loadData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [loadData]);
return <>{children(data, loading, error)}</>;
}
// Usage
<DataLoader loadData={fetchUser}>
{(user, loading, error) => {
if (loading) return <Loading />;
if (error) return <Error error={error} />;
return <UserProfile user={user} />;
}}
</DataLoader>
State Management Patterns
Local State
// Keep it simple when possible
function Counter() {
const [count, setCount] = useState(0);
return <Button onPress={() => setCount(c => c + 1)}>Count: {count}</Button>;
}
Shared State (Context)
// For cross-cutting concerns
const ThemeContext = createContext<ThemeContextValue>(null!);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
}
Global State (Zustand)
// For app-wide state
import { create } from 'zustand';
interface UserState {
user: User | null;
setUser: (user: User) => void;
logout: () => void;
}
export const useUserStore = create<UserState>((set) => ({
user: null,
setUser: (user) => set({ user }),
logout: () => set({ user: null }),
}));
Data Fetching Patterns
With Async/Await
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
// 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 <View>{data}</View>;
});
Lazy Loading
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}
Error Handling Patterns
Error Boundaries
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 <ErrorScreen error={this.state.error} />;
}
return this.props.children;
}
}
Try-Catch Pattern
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
import { useSafeAreaInsets } from 'react-native-safe-area-context';
function Screen() {
const insets = useSafeAreaInsets();
return (
<View style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>
{/* Content */}
</View>
);
}
Keyboard Avoiding
import { KeyboardAvoidingView, Platform } from 'react-native';
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{ flex: 1 }}
>
{/* Input form */}
</KeyboardAvoidingView>
Best Practices
- Keep Components Small: Single responsibility, easy to test
- Extract Custom Hooks: Reuse logic across components
- Use TypeScript: Catch errors early
- Handle Loading & Error States: Better user experience
- Clean Up Side Effects: Prevent memory leaks
- Optimize Wisely: Profile before optimizing
- 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