# React Native Reference (TypeScript) ## Project Structure ``` src/ ├── app/ │ ├── App.tsx # Root component, providers │ ├── navigation/ # React Navigation stacks + types │ └── store/ # RTK store setup ├── features/ │ └── home/ │ ├── api/ # RTK Query endpoints │ ├── components/ # Screen-specific components │ ├── hooks/ # Feature-level custom hooks │ ├── screens/ # Screen components │ ├── store/ # Zustand slice or RTK slice │ └── types.ts # Feature types ├── shared/ │ ├── components/ # Design system components │ ├── hooks/ # Shared hooks │ ├── theme/ # Colors, typography, spacing constants │ └── utils/ # Utilities └── services/ ├── api/ # Axios/fetch client + interceptors └── storage/ # MMKV wrapper ``` ## Navigation Setup (React Navigation v7) ```typescript export type RootStackParamList = { Auth: undefined; Home: undefined; Detail: { id: string }; Settings: undefined; }; export type RootStackScreenProps = NativeStackScreenProps; const Stack = createNativeStackNavigator(); export const RootNavigator = () => { const isLoggedIn = useAuthStore((s) => s.isLoggedIn); return ( {isLoggedIn ? ( <> ) : ( )} ); }; ``` ## State Management (Zustand + React Query) ```typescript // Client state — Zustand interface AuthState { token: string | null; isLoggedIn: boolean; setToken: (token: string) => void; logout: () => void; } export const useAuthStore = create()( persist( (set) => ({ token: null, isLoggedIn: false, setToken: (token) => set({ token, isLoggedIn: true }), logout: () => set({ token: null, isLoggedIn: false }), }), { name: 'auth-storage', storage: createJSONStorage(() => mmkvStorage) } ) ); // Server state — React Query export const useItems = () => useQuery({ queryKey: ['items'], queryFn: itemsApi.getAll, staleTime: 5 * 60 * 1000, // 5 minutes }); export const useRefreshItems = () => useMutation({ mutationFn: itemsApi.refresh, onSuccess: () => queryClient.invalidateQueries({ queryKey: ['items'] }), }); ``` ## Screen Pattern ```typescript type HomeScreenProps = RootStackScreenProps<'Home'>; export const HomeScreen: FC = ({ navigation }) => { const { data: items, isLoading, isError, refetch } = useItems(); if (isLoading) return ; if (isError) return ; return ( item.id} renderItem={({ item }) => ( navigation.navigate('Detail', { id: item.id })} /> )} ListEmptyComponent={} refreshControl={ } /> ); }; ``` ## API Client (Axios with interceptors) ```typescript const apiClient = axios.create({ baseURL: Config.API_BASE_URL, timeout: 10_000, headers: { 'Content-Type': 'application/json' }, }); // Auth token injection apiClient.interceptors.request.use((config) => { const token = useAuthStore.getState().token; if (token) config.headers.Authorization = `Bearer ${token}`; return config; }); // Token refresh on 401 apiClient.interceptors.response.use( (res) => res, async (error: AxiosError) => { if (error.response?.status === 401) { const newToken = await refreshToken(); if (newToken) { useAuthStore.getState().setToken(newToken); return apiClient(error.config!); } useAuthStore.getState().logout(); } return Promise.reject(error); } ); ``` ## API Response Validation (Zod) ```typescript const ItemSchema = z.object({ id: z.string(), title: z.string(), description: z.string().optional(), createdAt: z.string().datetime(), }); const ItemsResponseSchema = z.array(ItemSchema); type Item = z.infer; const getItems = async (): Promise => { const { data } = await apiClient.get('/items'); return ItemsResponseSchema.parse(data); // throws ZodError on invalid shape }; ``` ## Key Dependencies ```json { "dependencies": { "react-native": "0.74.x", "@react-navigation/native": "^7.0.0", "@react-navigation/native-stack": "^7.0.0", "@tanstack/react-query": "^5.45.0", "zustand": "^4.5.4", "axios": "^1.7.2", "zod": "^3.23.8", "react-native-mmkv": "^2.12.2", "react-native-safe-area-context": "^4.10.1", "react-native-screens": "^3.32.0" }, "devDependencies": { "typescript": "^5.4.5", "@testing-library/react-native": "^12.5.1", "msw": "^2.3.1", "jest": "^29.7.0" } } ``` ## New Architecture (Bridgeless) Notes - Enable New Architecture in `android/gradle.properties`: `newArchEnabled=true` - Use TurboModules for native modules; avoid legacy NativeModules API - Use Fabric for custom native views - Test with Hermes JS engine always enabled ## Performance Tips - Use `useCallback` + `memo` on `renderItem` / list item components - `FlatList` `windowSize`, `initialNumToRender`, `maxToRenderPerBatch` tuned - Avoid anonymous inline functions in JSX - `InteractionManager.runAfterInteractions` for heavy post-navigation work - `react-native-reanimated` for 60fps animations (runs on UI thread) ## Testing ```typescript describe('HomeScreen', () => { it('shows items when query succeeds', async () => { server.use( http.get(`${API_URL}/items`, () => HttpResponse.json([{ id: '1', title: 'Test Item' }]) ) ); const { getByText } = render( ); expect(await findByText('Test Item')).toBeTruthy(); }); }); ```