Agent skill
code-patterns-practices
React Native coding patterns, best practices, and common solutions for mobile development. Use when implementing features or refactoring code.
Stars
232
Forks
15
Install this agent skill to your Project
npx add-skill https://github.com/aiskillstore/marketplace/tree/main/skills/babakbar/code-patterns-practices
SKILL.md
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 (
<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
typescript
// 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
typescript
// 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)
typescript
// 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)
typescript
// 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
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 <View>{data}</View>;
});
Lazy Loading
typescript
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}
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 <ErrorScreen error={this.state.error} />;
}
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 (
<View style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>
{/* Content */}
</View>
);
}
Keyboard Avoiding
typescript
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
Resources
Didn't find tool you were looking for?