--- name: react-native description: React Native mobile patterns, platform-specific code --- # React Native Skill *Load with: base.md + typescript.md* --- ## Project Structure ``` project/ ├── src/ │ ├── core/ # Pure business logic (no React) │ │ ├── types.ts │ │ └── services/ │ ├── components/ # Reusable UI components │ │ ├── Button/ │ │ │ ├── Button.tsx │ │ │ ├── Button.test.tsx │ │ │ └── index.ts │ │ └── index.ts # Barrel export │ ├── screens/ # Screen components │ │ ├── Home/ │ │ │ ├── HomeScreen.tsx │ │ │ ├── useHome.ts # Screen-specific hook │ │ │ └── index.ts │ │ └── index.ts │ ├── navigation/ # Navigation configuration │ ├── hooks/ # Shared custom hooks │ ├── store/ # State management │ └── utils/ # Utilities ├── __tests__/ ├── android/ ├── ios/ └── CLAUDE.md ``` --- ## Component Patterns ### Functional Components Only ```typescript // Good - simple, testable interface ButtonProps { label: string; onPress: () => void; disabled?: boolean; } export function Button({ label, onPress, disabled = false }: ButtonProps): JSX.Element { return ( {label} ); } ``` ### Extract Logic to Hooks ```typescript // useHome.ts - all logic here export function useHome() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const refresh = useCallback(async () => { setLoading(true); const data = await fetchItems(); setItems(data); setLoading(false); }, []); return { items, loading, refresh }; } // HomeScreen.tsx - pure presentation export function HomeScreen(): JSX.Element { const { items, loading, refresh } = useHome(); return ( ); } ``` ### Props Interface Always Explicit ```typescript // Always define props interface, even if simple interface ItemCardProps { item: Item; onPress: (id: string) => void; } export function ItemCard({ item, onPress }: ItemCardProps): JSX.Element { ... } ``` --- ## State Management ### Local State First ```typescript // Start with useState, escalate only when needed const [value, setValue] = useState(''); ``` ### Zustand for Global State (if needed) ```typescript // store/useAppStore.ts import { create } from 'zustand'; interface AppState { user: User | null; setUser: (user: User | null) => void; } export const useAppStore = create((set) => ({ user: null, setUser: (user) => set({ user }), })); ``` ### React Query for Server State ```typescript // hooks/useItems.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; export function useItems() { return useQuery({ queryKey: ['items'], queryFn: fetchItems, }); } export function useCreateItem() { const queryClient = useQueryClient(); return useMutation({ mutationFn: createItem, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['items'] }); }, }); } ``` --- ## Testing ### Component Testing with React Native Testing Library ```typescript import { render, fireEvent } from '@testing-library/react-native'; import { Button } from './Button'; describe('Button', () => { it('calls onPress when pressed', () => { const onPress = jest.fn(); const { getByText } = render(