Top 30 React Native Interview Questions: Complete Guide 2026
The 30 most asked React Native interview questions. Detailed answers with code examples to land your mobile developer job.

React Native technical interviews assess cross-platform mobile development skills, iOS/Android specifics, and performance patterns. This guide covers the 30 most frequently asked questions, with detailed answers and code examples for effective preparation.
These questions cover fundamentals through advanced concepts. Mastering React Native architecture and understanding differences from React web are essential for interview success.
React Native Fundamentals
1. What is the difference between React and React Native?
React is a library for building web interfaces, while React Native enables native mobile app development for iOS and Android.
The fundamental difference lies in rendering: React uses a Virtual DOM that translates to HTML elements, while React Native uses a bridge that communicates with native components on each platform.
// React (Web) - uses HTML elements
function WebComponent() {
return (
<div className="container">
<span>Web text</span>
<button onClick={handleClick}>Click</button>
</div>
)
}
// React Native - uses native components
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
function NativeComponent() {
return (
<View style={styles.container}>
<Text>Native text</Text>
<TouchableOpacity onPress={handlePress}>
<Text>Press</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16
}
})React Native components translate to UIView on iOS and android.view on Android, delivering native performance.
2. How does React Native's architecture work?
React Native uses a three-layer architecture: JavaScript, Bridge (or JSI in the new architecture), and Native.
JavaScript code runs in a JS engine (Hermes or JavaScriptCore). Communications with native code pass through JSON serialization in the old architecture, or through JSI (JavaScript Interface) in the new architecture.
// Old architecture: asynchronous communication via Bridge
// The Bridge serializes messages as JSON between JS and Native
// New architecture (Fabric + TurboModules)
// JSI enables synchronous direct calls to native modules
// Example TurboModule usage
import { TurboModuleRegistry } from 'react-native'
// Synchronous access to native module
const DeviceInfo = TurboModuleRegistry.get('DeviceInfo')
const deviceName = DeviceInfo.getDeviceName() // Synchronous call
// With Fabric, rendering is smoother
// Components can be created synchronously
// Reducing jank during animationsThe new architecture significantly improves performance by eliminating JSON serialization and enabling synchronous calls.
3. What is the Metro bundler?
Metro is the JavaScript bundler used by React Native. It transforms source code into an optimized bundle for mobile execution.
Metro handles module resolution, code transformation (via Babel), and hot reloading during development.
const { getDefaultConfig } = require('expo/metro-config')
const config = getDefaultConfig(__dirname)
// Custom configuration
config.resolver.assetExts.push('db') // Add extensions
config.resolver.sourceExts.push('cjs') // CommonJS support
// Transformer configuration
config.transformer.babelTransformerPath = require.resolve(
'react-native-svg-transformer'
)
// Production optimizations
config.transformer.minifierConfig = {
keep_classnames: true,
keep_fnames: true,
mangle: {
keep_classnames: true,
keep_fnames: true
}
}
module.exports = configMetro supports Fast Refresh, allowing instant changes without losing application state.
4. Explain StyleSheet.create and its benefits
StyleSheet.create optimizes styles by validating them and converting them to numeric references, reducing bridge overhead.
// ❌ Inline styles - recreated on every render
function BadExample() {
return (
<View style={{ flex: 1, padding: 16, backgroundColor: '#fff' }}>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>Title</Text>
</View>
)
}
// ✅ StyleSheet.create - optimized and validated
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#fff'
},
title: {
fontSize: 18,
fontWeight: 'bold'
},
// Style composition
row: {
flexDirection: 'row',
alignItems: 'center',
gap: 8
}
})
function GoodExample() {
return (
<View style={styles.container}>
<Text style={styles.title}>Title</Text>
{/* Style combination */}
<View style={[styles.row, { marginTop: 10 }]}>
<Text>Content</Text>
</View>
</View>
)
}
// StyleSheet.absoluteFillObject for absolute positioning
const overlayStyles = StyleSheet.create({
overlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0,0,0,0.5)'
}
})5. What is the difference between web Flexbox and React Native?
React Native uses Flexbox but with different default values from web, adapted for vertical mobile interfaces.
// Key differences from web
const styles = StyleSheet.create({
container: {
// flexDirection: 'column' by default (vs 'row' on web)
// alignItems: 'stretch' by default
flex: 1
},
// React Native Flexbox
row: {
flexDirection: 'row', // Horizontal
justifyContent: 'space-between', // Main axis
alignItems: 'center', // Cross axis
flexWrap: 'wrap', // Line wrapping
gap: 8 // Supported since RN 0.71
},
// Flex grow/shrink
flexItem: {
flex: 1, // Equivalent to flex: 1 1 0
flexGrow: 1, // Grow to fill
flexShrink: 0, // Don't shrink
flexBasis: 100 // Base size
},
// Absolute positioning
absolute: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0
}
})
// Practical example: card with image and content
function Card() {
return (
<View style={cardStyles.container}>
<Image source={{ uri: imageUrl }} style={cardStyles.image} />
<View style={cardStyles.content}>
<Text style={cardStyles.title}>Title</Text>
<Text style={cardStyles.description}>Description</Text>
</View>
</View>
)
}
const cardStyles = StyleSheet.create({
container: {
flexDirection: 'row',
backgroundColor: '#fff',
borderRadius: 8,
overflow: 'hidden'
},
image: {
width: 100,
height: 100
},
content: {
flex: 1, // Takes remaining space
padding: 12,
justifyContent: 'center'
},
title: {
fontSize: 16,
fontWeight: '600'
},
description: {
fontSize: 14,
color: '#666'
}
})Navigation and Components
6. How do you implement navigation with React Navigation?
React Navigation is the standard solution for navigation in React Native. It offers various navigator types suited to mobile patterns.
// Installing dependencies
// npm install @react-navigation/native @react-navigation/native-stack
// npm install react-native-screens react-native-safe-area-context
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
// TypeScript typing for navigation params
type RootStackParamList = {
Home: undefined
Profile: { userId: string }
Settings: { section?: string }
}
const Stack = createNativeStackNavigator<RootStackParamList>()
const Tab = createBottomTabNavigator()
// Tab navigation
function TabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
// Dynamic icon based on tab
const iconName = route.name === 'Home' ? 'home' : 'settings'
return <Icon name={iconName} size={size} color={color} />
},
tabBarActiveTintColor: '#007AFF',
tabBarInactiveTintColor: 'gray'
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
)
}
// Stack navigation
function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: '#007AFF' },
headerTintColor: '#fff',
animation: 'slide_from_right' // Native animation
}}
>
<Stack.Screen
name="Home"
component={TabNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route }) => ({
title: `Profile ${route.params.userId}`
})}
/>
</Stack.Navigator>
</NavigationContainer>
)
}7. How do you handle performant lists with FlatList?
FlatList is optimized for long lists with automatic virtualization, only rendering visible elements.
import { FlatList, RefreshControl } from 'react-native'
function ProductList() {
const [products, setProducts] = useState([])
const [refreshing, setRefreshing] = useState(false)
const [loading, setLoading] = useState(false)
// Initial loading
const fetchProducts = async (page = 1) => {
const response = await api.getProducts(page)
return response.data
}
// Pull-to-refresh
const onRefresh = useCallback(async () => {
setRefreshing(true)
const data = await fetchProducts(1)
setProducts(data)
setRefreshing(false)
}, [])
// Infinite pagination
const loadMore = useCallback(async () => {
if (loading) return
setLoading(true)
const nextPage = Math.ceil(products.length / 20) + 1
const data = await fetchProducts(nextPage)
setProducts(prev => [...prev, ...data])
setLoading(false)
}, [products.length, loading])
// Item rendering
const renderItem = useCallback(({ item }) => (
<ProductCard product={item} />
), [])
// Key extraction
const keyExtractor = useCallback((item) => item.id.toString(), [])
// Item separator
const ItemSeparator = useCallback(() => (
<View style={{ height: 12 }} />
), [])
return (
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={keyExtractor}
ItemSeparatorComponent={ItemSeparator}
// Performance optimizations
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
windowSize={5}
// Pull-to-refresh
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor="#007AFF"
/>
}
// Infinite pagination
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loading ? <ActivityIndicator /> : null}
// Empty list
ListEmptyComponent={<EmptyState message="No products" />}
/>
)
}Always memoize renderItem with useCallback and extract heavy components. Avoid inline functions in renderItem that cause unnecessary re-renders.
8. What is the difference between TouchableOpacity, Pressable, and TouchableHighlight?
These components handle touch interactions with different visual feedback.
import {
TouchableOpacity,
TouchableHighlight,
Pressable,
StyleSheet
} from 'react-native'
function InteractionExamples() {
return (
<View style={styles.container}>
{/* TouchableOpacity: reduces opacity on touch */}
<TouchableOpacity
activeOpacity={0.7}
onPress={() => console.log('Pressed')}
style={styles.button}
>
<Text>TouchableOpacity</Text>
</TouchableOpacity>
{/* TouchableHighlight: adds background color */}
<TouchableHighlight
underlayColor="#ddd"
onPress={() => console.log('Pressed')}
style={styles.button}
>
<Text>TouchableHighlight</Text>
</TouchableHighlight>
{/* Pressable: modern API with more control */}
<Pressable
onPress={() => console.log('Pressed')}
onLongPress={() => console.log('Long press')}
delayLongPress={500}
style={({ pressed }) => [
styles.button,
pressed && styles.buttonPressed
]}
>
{({ pressed }) => (
<Text style={pressed && styles.textPressed}>
{pressed ? 'Pressed!' : 'Pressable'}
</Text>
)}
</Pressable>
{/* Pressable with hitSlop to enlarge touch area */}
<Pressable
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
onPress={() => console.log('Pressed')}
style={styles.smallButton}
>
<Text>Small button</Text>
</Pressable>
</View>
)
}
const styles = StyleSheet.create({
container: {
gap: 16,
padding: 20
},
button: {
backgroundColor: '#007AFF',
padding: 16,
borderRadius: 8,
alignItems: 'center'
},
buttonPressed: {
backgroundColor: '#0056b3',
transform: [{ scale: 0.98 }]
},
textPressed: {
color: '#fff'
},
smallButton: {
padding: 8,
backgroundColor: '#eee'
}
})Pressable is recommended for new projects as it offers more control and a more consistent API.
9. How do you create smooth animations?
React Native offers several animation APIs: Animated (built-in) and Reanimated (more performant).
import { Animated, Easing } from 'react-native'
import Reanimated, {
useSharedValue,
useAnimatedStyle,
withSpring,
withTiming
} from 'react-native-reanimated'
// Animation with Animated (native API)
function FadeInView({ children }) {
const fadeAnim = useRef(new Animated.Value(0)).current
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 500,
easing: Easing.ease,
useNativeDriver: true // Performant on UI thread
}).start()
}, [])
return (
<Animated.View style={{ opacity: fadeAnim }}>
{children}
</Animated.View>
)
}
// Animation with Reanimated (recommended for complex animations)
function BouncyButton() {
const scale = useSharedValue(1)
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }]
}))
const handlePressIn = () => {
scale.value = withSpring(0.95, {
damping: 10,
stiffness: 400
})
}
const handlePressOut = () => {
scale.value = withSpring(1, {
damping: 10,
stiffness: 400
})
}
return (
<Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}>
<Reanimated.View style={[styles.button, animatedStyle]}>
<Text style={styles.buttonText}>Press</Text>
</Reanimated.View>
</Pressable>
)
}
// List animation with LayoutAnimation
import { LayoutAnimation, UIManager, Platform } from 'react-native'
// Enable on Android
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental?.(true)
}
function AnimatedList() {
const [items, setItems] = useState([])
const addItem = () => {
// Configure animation before state change
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring)
setItems(prev => [...prev, { id: Date.now() }])
}
const removeItem = (id) => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
setItems(prev => prev.filter(item => item.id !== id))
}
return (
<View>
<Button title="Add" onPress={addItem} />
{items.map(item => (
<TouchableOpacity key={item.id} onPress={() => removeItem(item.id)}>
<View style={styles.item}>
<Text>Item {item.id}</Text>
</View>
</TouchableOpacity>
))}
</View>
)
}Ready to ace your React Native interviews?
Practice with our interactive simulators, flashcards, and technical tests.
State and Data Management
10. How do you manage global state in React Native?
The same solutions as React web apply: Context API, Redux, Zustand, or MobX.
// Lightweight solution with Zustand
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import AsyncStorage from '@react-native-async-storage/async-storage'
// Store with persistence
const useAuthStore = create(
persist(
(set, get) => ({
user: null,
token: null,
isAuthenticated: false,
login: async (email, password) => {
const response = await api.login(email, password)
set({
user: response.user,
token: response.token,
isAuthenticated: true
})
},
logout: () => {
set({ user: null, token: null, isAuthenticated: false })
},
updateProfile: (updates) => {
set(state => ({
user: { ...state.user, ...updates }
}))
}
}),
{
name: 'auth-storage',
storage: createJSONStorage(() => AsyncStorage)
}
)
)
// Usage in a component
function ProfileScreen() {
const { user, logout, updateProfile } = useAuthStore()
if (!user) return <LoginPrompt />
return (
<View style={styles.container}>
<Text style={styles.name}>{user.name}</Text>
<Text style={styles.email}>{user.email}</Text>
<TouchableOpacity onPress={logout} style={styles.logoutButton}>
<Text>Logout</Text>
</TouchableOpacity>
</View>
)
}
// Cart store
const useCartStore = create((set, get) => ({
items: [],
addItem: (product) => set(state => {
const existing = state.items.find(i => i.id === product.id)
if (existing) {
return {
items: state.items.map(i =>
i.id === product.id
? { ...i, quantity: i.quantity + 1 }
: i
)
}
}
return { items: [...state.items, { ...product, quantity: 1 }] }
}),
removeItem: (id) => set(state => ({
items: state.items.filter(i => i.id !== id)
})),
getTotal: () => {
return get().items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
},
clearCart: () => set({ items: [] })
}))11. How do you make API calls with cache management?
React Query (TanStack Query) is the recommended solution for server data management.
import { QueryClient, QueryClientProvider, useQuery, useMutation } from '@tanstack/react-query'
// Client configuration
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 30 * 60 * 1000, // 30 minutes
retry: 2,
refetchOnWindowFocus: false // Mobile doesn't have "window focus"
}
}
})
// Provider in App.tsx
function App() {
return (
<QueryClientProvider client={queryClient}>
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
</QueryClientProvider>
)
}
// Custom hook for products
function useProducts(categoryId) {
return useQuery({
queryKey: ['products', categoryId],
queryFn: async () => {
const response = await fetch(`/api/products?category=${categoryId}`)
if (!response.ok) throw new Error('Network error')
return response.json()
},
enabled: !!categoryId // Don't execute if no categoryId
})
}
// Mutation with cache invalidation
function useAddToCart() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (product) => {
const response = await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify(product)
})
return response.json()
},
onSuccess: () => {
// Invalidate cart cache to force refetch
queryClient.invalidateQueries({ queryKey: ['cart'] })
},
onError: (error) => {
Alert.alert('Error', error.message)
}
})
}
// Usage in a component
function ProductListScreen({ categoryId }) {
const { data: products, isLoading, error, refetch } = useProducts(categoryId)
const addToCart = useAddToCart()
if (isLoading) return <LoadingSpinner />
if (error) return <ErrorView error={error} onRetry={refetch} />
return (
<FlatList
data={products}
renderItem={({ item }) => (
<ProductCard
product={item}
onAddToCart={() => addToCart.mutate(item)}
isAddingToCart={addToCart.isPending}
/>
)}
refreshing={isLoading}
onRefresh={refetch}
/>
)
}12. How do you store data locally?
Several options exist depending on data type: AsyncStorage for simple data, MMKV for performance, and SQLite for structured data.
// AsyncStorage - simple but slow for large volumes
import AsyncStorage from '@react-native-async-storage/async-storage'
const storage = {
async get(key) {
const value = await AsyncStorage.getItem(key)
return value ? JSON.parse(value) : null
},
async set(key, value) {
await AsyncStorage.setItem(key, JSON.stringify(value))
},
async remove(key) {
await AsyncStorage.removeItem(key)
},
async clear() {
await AsyncStorage.clear()
}
}
// MMKV - very performant (recommended)
import { MMKV } from 'react-native-mmkv'
const mmkv = new MMKV()
const fastStorage = {
get(key) {
const value = mmkv.getString(key)
return value ? JSON.parse(value) : null
},
set(key, value) {
mmkv.set(key, JSON.stringify(value))
},
remove(key) {
mmkv.delete(key)
},
// Primitive type support
getNumber(key) {
return mmkv.getNumber(key)
},
setNumber(key, value) {
mmkv.set(key, value)
},
getBoolean(key) {
return mmkv.getBoolean(key)
}
}
// Hook to use MMKV with React
function useMMKVStorage(key, defaultValue) {
const [value, setValue] = useState(() => {
const stored = fastStorage.get(key)
return stored ?? defaultValue
})
const setStoredValue = useCallback((newValue) => {
setValue(newValue)
fastStorage.set(key, newValue)
}, [key])
return [value, setStoredValue]
}
// Usage
function SettingsScreen() {
const [theme, setTheme] = useMMKVStorage('theme', 'light')
const [notifications, setNotifications] = useMMKVStorage('notifications', true)
return (
<View>
<Switch
value={theme === 'dark'}
onValueChange={(v) => setTheme(v ? 'dark' : 'light')}
/>
<Switch
value={notifications}
onValueChange={setNotifications}
/>
</View>
)
}For tokens and sensitive data, use expo-secure-store or react-native-keychain which encrypt data via Keychain (iOS) and Keystore (Android).
Performance and Optimization
13. How do you optimize React Native app performance?
Optimization covers several aspects: rendering, memory, and interactions.
const ProductCard = React.memo(function ProductCard({ product, onPress }) {
return (
<TouchableOpacity onPress={() => onPress(product.id)}>
<View style={styles.card}>
<Image source={{ uri: product.image }} style={styles.image} />
<Text style={styles.title}>{product.name}</Text>
<Text style={styles.price}>{product.price}$</Text>
</View>
</TouchableOpacity>
)
}, (prevProps, nextProps) => {
// Custom comparison
return prevProps.product.id === nextProps.product.id
})
// 2. Memoize callbacks
function ProductList({ products }) {
// ❌ New function on every render
// onPress={(id) => handlePress(id)}
// ✅ Stable function
const handlePress = useCallback((id) => {
navigation.navigate('Product', { id })
}, [navigation])
return (
<FlatList
data={products}
renderItem={({ item }) => (
<ProductCard product={item} onPress={handlePress} />
)}
/>
)
}
// 3. Optimize images
import FastImage from 'react-native-fast-image'
function OptimizedImage({ uri }) {
return (
<FastImage
source={{
uri,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable
}}
style={styles.image}
resizeMode={FastImage.resizeMode.cover}
/>
)
}
// 4. Use InteractionManager for heavy tasks
import { InteractionManager } from 'react-native'
function HeavyScreen() {
const [data, setData] = useState(null)
useEffect(() => {
// Wait for animations to complete
const task = InteractionManager.runAfterInteractions(() => {
const result = performHeavyComputation()
setData(result)
})
return () => task.cancel()
}, [])
return data ? <DataView data={data} /> : <LoadingView />
}
// 5. Lazy loading screens
const HeavyScreen = React.lazy(() => import('./HeavyScreen'))
function Navigator() {
return (
<Stack.Navigator>
<Stack.Screen
name="Heavy"
component={HeavyScreen}
options={{ lazy: true }}
/>
</Stack.Navigator>
)
}14. How do you debug performance issues?
React Native provides several tools to identify bottlenecks.
// Configuration in android/app/build.gradle and ios/Podfile
// See: https://fbflipper.com/
// 2. Console.time to measure operations
function fetchData() {
console.time('fetchData')
const data = await api.getData()
console.timeEnd('fetchData') // fetchData: 234ms
return data
}
// 3. Performance monitor (shake device → Show Perf Monitor)
// Shows JS and UI FPS
// 4. Hermes profiler for CPU
// Enable in metro.config.js
module.exports = {
transformer: {
hermesParser: true
}
}
// 5. Identify re-renders with why-did-you-render
// Installation: npm install @welldone-software/why-did-you-render
import React from 'react'
if (__DEV__) {
const whyDidYouRender = require('@welldone-software/why-did-you-render')
whyDidYouRender(React, {
trackAllPureComponents: true
})
}
// Mark a component to monitor
ProductCard.whyDidYouRender = true
// 6. Measure mount time
function useComponentTiming(componentName) {
const mountTime = useRef(Date.now())
useEffect(() => {
const duration = Date.now() - mountTime.current
console.log(`${componentName} mounted in ${duration}ms`)
return () => {
console.log(`${componentName} unmounted`)
}
}, [componentName])
}
// Usage
function MyComponent() {
useComponentTiming('MyComponent')
// ...
}15. How do you handle offline mode?
Offline handling requires a caching and synchronization strategy.
import NetInfo from '@react-native-community/netinfo'
// Hook to monitor connectivity
function useNetworkStatus() {
const [isConnected, setIsConnected] = useState(true)
const [connectionType, setConnectionType] = useState(null)
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsConnected(state.isConnected)
setConnectionType(state.type)
})
return () => unsubscribe()
}, [])
return { isConnected, connectionType }
}
// Service with offline queue
class OfflineQueue {
constructor() {
this.queue = []
this.isProcessing = false
}
async add(action) {
this.queue.push({
id: Date.now(),
action,
timestamp: new Date().toISOString()
})
await this.persist()
}
async persist() {
await AsyncStorage.setItem('offline_queue', JSON.stringify(this.queue))
}
async load() {
const data = await AsyncStorage.getItem('offline_queue')
this.queue = data ? JSON.parse(data) : []
}
async process() {
if (this.isProcessing || this.queue.length === 0) return
this.isProcessing = true
const { isConnected } = await NetInfo.fetch()
if (!isConnected) {
this.isProcessing = false
return
}
while (this.queue.length > 0) {
const item = this.queue[0]
try {
await this.executeAction(item.action)
this.queue.shift()
await this.persist()
} catch (error) {
console.error('Failed to process action:', error)
break
}
}
this.isProcessing = false
}
async executeAction(action) {
switch (action.type) {
case 'CREATE_ORDER':
return api.createOrder(action.payload)
case 'UPDATE_PROFILE':
return api.updateProfile(action.payload)
default:
throw new Error(`Unknown action: ${action.type}`)
}
}
}
const offlineQueue = new OfflineQueue()
// Network status banner component
function NetworkBanner() {
const { isConnected } = useNetworkStatus()
if (isConnected) return null
return (
<View style={styles.banner}>
<Text style={styles.bannerText}>
Offline mode - Changes will be synchronized
</Text>
</View>
)
}Mobile Specifics
16. How do you handle permissions on iOS and Android?
Permissions are handled differently on each platform. Libraries like react-native-permissions unify the API.
import { Platform, Alert, Linking } from 'react-native'
import {
check,
request,
PERMISSIONS,
RESULTS,
openSettings
} from 'react-native-permissions'
// Permission configuration per platform
const PERMISSION_TYPES = {
camera: Platform.select({
ios: PERMISSIONS.IOS.CAMERA,
android: PERMISSIONS.ANDROID.CAMERA
}),
photos: Platform.select({
ios: PERMISSIONS.IOS.PHOTO_LIBRARY,
android: PERMISSIONS.ANDROID.READ_MEDIA_IMAGES
}),
location: Platform.select({
ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE,
android: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
})
}
// Hook to manage permissions
function usePermission(type) {
const [status, setStatus] = useState(RESULTS.UNAVAILABLE)
const permission = PERMISSION_TYPES[type]
useEffect(() => {
check(permission).then(setStatus)
}, [permission])
const requestPermission = useCallback(async () => {
const result = await request(permission)
setStatus(result)
if (result === RESULTS.BLOCKED) {
Alert.alert(
'Permission required',
`This feature requires ${type} access. Do you want to open settings?`,
[
{ text: 'No', style: 'cancel' },
{ text: 'Open', onPress: openSettings }
]
)
}
return result
}, [permission, type])
return {
status,
isGranted: status === RESULTS.GRANTED,
isDenied: status === RESULTS.DENIED,
isBlocked: status === RESULTS.BLOCKED,
requestPermission
}
}
// Usage
function CameraButton() {
const { isGranted, requestPermission } = usePermission('camera')
const handlePress = async () => {
if (!isGranted) {
const result = await requestPermission()
if (result !== RESULTS.GRANTED) return
}
// Open camera
navigation.navigate('Camera')
}
return (
<TouchableOpacity onPress={handlePress}>
<Text>Take a photo</Text>
</TouchableOpacity>
)
}17. How do you implement push notifications?
Push notifications require native configuration and a backend for sending.
import messaging from '@react-native-firebase/messaging'
import notifee, { AndroidImportance } from '@notifee/react-native'
// Notification service
class NotificationService {
async initialize() {
// Request permission (iOS)
const authStatus = await messaging().requestPermission()
const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED
if (enabled) {
// Get FCM token
const token = await messaging().getToken()
await this.registerToken(token)
// Listen for token changes
messaging().onTokenRefresh(this.registerToken)
// Create Android channel
await notifee.createChannel({
id: 'default',
name: 'Notifications',
importance: AndroidImportance.HIGH
})
}
return enabled
}
async registerToken(token) {
// Send token to backend
await api.registerPushToken(token)
}
// Setup handlers
setupHandlers() {
// Notification received in foreground
messaging().onMessage(async (remoteMessage) => {
await this.displayNotification(remoteMessage)
})
// Notification tapped (app in background)
messaging().onNotificationOpenedApp((remoteMessage) => {
this.handleNotificationPress(remoteMessage)
})
// App opened from notification (app closed)
messaging()
.getInitialNotification()
.then((remoteMessage) => {
if (remoteMessage) {
this.handleNotificationPress(remoteMessage)
}
})
}
async displayNotification(remoteMessage) {
const { title, body } = remoteMessage.notification
await notifee.displayNotification({
title,
body,
android: {
channelId: 'default',
pressAction: { id: 'default' }
},
data: remoteMessage.data
})
}
handleNotificationPress(remoteMessage) {
const { type, id } = remoteMessage.data
switch (type) {
case 'order':
navigation.navigate('OrderDetail', { orderId: id })
break
case 'message':
navigation.navigate('Chat', { conversationId: id })
break
}
}
}
// Usage in App.tsx
function App() {
useEffect(() => {
const notificationService = new NotificationService()
notificationService.initialize()
notificationService.setupHandlers()
}, [])
return <AppNavigator />
}18. How do you handle deep links?
Deep links allow opening the app to a specific screen from an external URL.
import { Linking } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
// Deep link configuration
const linking = {
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: 'home',
Product: {
path: 'product/:id',
parse: {
id: (id) => parseInt(id, 10)
}
},
Profile: 'profile/:userId?',
Settings: {
path: 'settings',
screens: {
Notifications: 'notifications',
Privacy: 'privacy'
}
}
}
},
// Custom function to get initial URL
async getInitialURL() {
// Check if app was opened via deep link
const url = await Linking.getInitialURL()
if (url) return url
// Check notifications
const message = await messaging().getInitialNotification()
if (message?.data?.link) return message.data.link
return null
},
// Subscribe to incoming links
subscribe(listener) {
// Standard deep links
const linkingSubscription = Linking.addEventListener('url', ({ url }) => {
listener(url)
})
// Links from notifications
const unsubscribeNotification = messaging().onNotificationOpenedApp(
(message) => {
const link = message.data?.link
if (link) listener(link)
}
)
return () => {
linkingSubscription.remove()
unsubscribeNotification()
}
}
}
// Usage
function App() {
return (
<NavigationContainer linking={linking} fallback={<LoadingScreen />}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Product" component={ProductScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}
// Testing deep links
// myapp://product/123
// https://myapp.com/profile/user456Ready to ace your React Native interviews?
Practice with our interactive simulators, flashcards, and technical tests.
Native Code and Modules
19. When and how do you write native code?
Native code is necessary to access features not available in JavaScript or to optimize critical performance.
// iOS - Native module in Swift
// ios/MyModule.swift
import Foundation
@objc(MyModule)
class MyModule: NSObject {
@objc
func getDeviceInfo(_ resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
let info: [String: Any] = [
"model": UIDevice.current.model,
"systemVersion": UIDevice.current.systemVersion,
"name": UIDevice.current.name
]
resolve(info)
}
@objc
static func requiresMainQueueSetup() -> Bool {
return false
}
}
// ios/MyModule.m (Bridge)
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(MyModule, NSObject)
RCT_EXTERN_METHOD(getDeviceInfo:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
@end
// Android - Native module in Kotlin
// android/app/src/main/java/com/myapp/MyModule.kt
package com.myapp
import com.facebook.react.bridge.*
class MyModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
override fun getName() = "MyModule"
@ReactMethod
fun getDeviceInfo(promise: Promise) {
val info = Arguments.createMap().apply {
putString("model", android.os.Build.MODEL)
putString("systemVersion", android.os.Build.VERSION.RELEASE)
putString("manufacturer", android.os.Build.MANUFACTURER)
}
promise.resolve(info)
}
}
// JavaScript - Using native module
import { NativeModules } from 'react-native'
const { MyModule } = NativeModules
async function getDeviceInfo() {
try {
const info = await MyModule.getDeviceInfo()
console.log('Device info:', info)
return info
} catch (error) {
console.error('Error getting device info:', error)
throw error
}
}20. What is Expo and when should you use it?
Expo is a framework that simplifies React Native development by managing native configuration.
// Expo benefits
// - No need for Android Studio or Xcode to start
// - OTA (over-the-air) updates without app stores
// - Rich SDK with preconfigured modules
// - EAS Build for cloud builds
// Creating an Expo project
// npx create-expo-app@latest MyApp
// Using Expo modules
import * as ImagePicker from 'expo-image-picker'
import * as Location from 'expo-location'
import * as Notifications from 'expo-notifications'
async function pickImage() {
// Request permission
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync()
if (status !== 'granted') {
alert('Permission denied')
return
}
// Open image picker
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 0.8
})
if (!result.canceled) {
return result.assets[0].uri
}
}
// Build configuration
// app.json
{
"expo": {
"name": "MyApp",
"slug": "myapp",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.company.myapp"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.company.myapp"
},
"plugins": [
"expo-router",
[
"expo-camera",
{
"cameraPermission": "Allow camera access"
}
]
]
}
}
// When NOT to use Expo
// - Need for complex custom native modules
// - Integration with specific native SDKs
// - Total control over native configuration
// - Very lightweight application (Expo adds weight)21. How does Hot Reloading work?
Hot Reloading (Fast Refresh) allows instant changes without losing application state.
// Fast Refresh preserves hooks state
function Counter() {
const [count, setCount] = useState(0)
// Modify this text and save
// The count state will be preserved
return (
<View>
<Text>Counter: {count}</Text>
<Button title="+1" onPress={() => setCount(c => c + 1)} />
</View>
)
}
// ⚠️ Cases where Fast Refresh does full reload
// 1. Syntax error
// 2. Modifying a class component
// 3. File with mixed non-component exports
// ❌ This file will do full reload because of mixed exports
export const API_URL = 'https://api.example.com'
export function MyComponent() { /* ... */ }
// ✅ Separate into distinct files
// constants.js
export const API_URL = 'https://api.example.com'
// MyComponent.js
export function MyComponent() { /* ... */ }
// Force remount if needed
// Add this comment at top of file:
// @refresh reset
// Metro configuration for Fast Refresh
// metro.config.js
module.exports = {
transformer: {
experimentalImportSupport: false,
inlineRequires: true
}
}Testing and Quality
22. How do you test a React Native application?
React Native testing uses Jest with specialized libraries for rendering and interactions.
// Jest configuration
// jest.config.js
module.exports = {
preset: 'react-native',
setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|@react-native|@react-navigation)/)'
],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js'
}
}
// Component test with React Native Testing Library
import { render, screen, fireEvent, waitFor } from '@testing-library/react-native'
import { ProductCard } from './ProductCard'
describe('ProductCard', () => {
const mockProduct = {
id: '1',
name: 'iPhone 15',
price: 999,
image: 'https://example.com/iphone.jpg'
}
it('renders product information', () => {
render(<ProductCard product={mockProduct} />)
expect(screen.getByText('iPhone 15')).toBeOnTheScreen()
expect(screen.getByText('$999')).toBeOnTheScreen()
})
it('calls onPress when tapped', () => {
const onPress = jest.fn()
render(<ProductCard product={mockProduct} onPress={onPress} />)
fireEvent.press(screen.getByTestId('product-card'))
expect(onPress).toHaveBeenCalledWith('1')
})
it('shows loading state when adding to cart', async () => {
const onAddToCart = jest.fn(() => new Promise(r => setTimeout(r, 100)))
render(<ProductCard product={mockProduct} onAddToCart={onAddToCart} />)
fireEvent.press(screen.getByText('Add to cart'))
expect(screen.getByTestId('loading-indicator')).toBeOnTheScreen()
await waitFor(() => {
expect(screen.queryByTestId('loading-indicator')).not.toBeOnTheScreen()
})
})
})
// Navigation test
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
const Stack = createNativeStackNavigator()
function renderWithNavigation(component, { initialRouteName = 'Test' } = {}) {
return render(
<NavigationContainer>
<Stack.Navigator initialRouteName={initialRouteName}>
<Stack.Screen name="Test" component={component} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}
// Custom hook test
import { renderHook, act } from '@testing-library/react-native'
import { useCounter } from './useCounter'
test('increments counter', () => {
const { result } = renderHook(() => useCounter(0))
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})23. How do you implement E2E tests with Detox?
Detox allows testing the application on real simulators/emulators.
// Installation
// npm install detox --save-dev
// detox init -r jest
// .detoxrc.js
module.exports = {
testRunner: {
args: {
$0: 'jest',
config: 'e2e/jest.config.js'
},
jest: {
setupTimeout: 120000
}
},
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/MyApp.app',
build: 'xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build'
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug'
}
},
devices: {
simulator: {
type: 'ios.simulator',
device: { type: 'iPhone 15' }
},
emulator: {
type: 'android.emulator',
device: { avdName: 'Pixel_5_API_34' }
}
}
}
// e2e/login.test.js
describe('Login Flow', () => {
beforeAll(async () => {
await device.launchApp()
})
beforeEach(async () => {
await device.reloadReactNative()
})
it('should login successfully with valid credentials', async () => {
// Fill the form
await element(by.id('email-input')).typeText('test@example.com')
await element(by.id('password-input')).typeText('password123')
// Submit
await element(by.id('login-button')).tap()
// Verify navigation to dashboard
await waitFor(element(by.text('Welcome')))
.toBeVisible()
.withTimeout(5000)
})
it('should show error with invalid credentials', async () => {
await element(by.id('email-input')).typeText('wrong@example.com')
await element(by.id('password-input')).typeText('wrongpassword')
await element(by.id('login-button')).tap()
await expect(element(by.text('Invalid credentials'))).toBeVisible()
})
it('should navigate to forgot password', async () => {
await element(by.id('forgot-password-link')).tap()
await expect(element(by.text('Reset Password'))).toBeVisible()
})
})
// Detox commands
// detox build --configuration ios.debug
// detox test --configuration ios.debugDeployment and Production
24. How do you manage environments (dev, staging, prod)?
Environment management requires distinct configuration variables.
// Option 1: react-native-config
// .env.development
API_URL=https://dev-api.myapp.com
ANALYTICS_KEY=dev_key
// .env.staging
API_URL=https://staging-api.myapp.com
ANALYTICS_KEY=staging_key
// .env.production
API_URL=https://api.myapp.com
ANALYTICS_KEY=prod_key
// Usage
import Config from 'react-native-config'
const api = {
baseUrl: Config.API_URL,
analyticsKey: Config.ANALYTICS_KEY
}
// Option 2: JavaScript configuration file
// config/index.js
const ENV = {
development: {
apiUrl: 'https://dev-api.myapp.com',
analyticsEnabled: false,
logLevel: 'debug'
},
staging: {
apiUrl: 'https://staging-api.myapp.com',
analyticsEnabled: true,
logLevel: 'info'
},
production: {
apiUrl: 'https://api.myapp.com',
analyticsEnabled: true,
logLevel: 'error'
}
}
const getEnv = () => {
if (__DEV__) return 'development'
// Logic to determine staging vs prod
return 'production'
}
export const config = ENV[getEnv()]
// Option 3: Expo with app.config.js
// app.config.js
export default ({ config }) => {
const env = process.env.APP_ENV || 'development'
const envConfig = {
development: {
apiUrl: 'https://dev-api.myapp.com',
bundleId: 'com.myapp.dev'
},
production: {
apiUrl: 'https://api.myapp.com',
bundleId: 'com.myapp'
}
}
return {
...config,
extra: {
...envConfig[env],
env
},
ios: {
bundleIdentifier: envConfig[env].bundleId
},
android: {
package: envConfig[env].bundleId
}
}
}25. How do you deploy to app stores?
Deployment involves build configuration, metadata, and submission.
# Option 1: EAS Build (Expo)
# Installation
npm install -g eas-cli
# Configuration
eas build:configure
# eas.json
{
"cli": {
"version": ">= 5.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {
"ios": {
"appleId": "your@email.com",
"ascAppId": "1234567890"
},
"android": {
"serviceAccountKeyPath": "./google-services.json",
"track": "production"
}
}
}
}
# Production build
eas build --platform all --profile production
# Store submission
eas submit --platform all --profile production
# Option 2: Fastlane (React Native CLI)
# Gemfile
source "https://rubygems.org"
gem "fastlane"
# ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Deploy to TestFlight"
lane :beta do
increment_build_number(xcodeproj: "MyApp.xcodeproj")
build_app(scheme: "MyApp")
upload_to_testflight
end
desc "Deploy to App Store"
lane :release do
increment_build_number(xcodeproj: "MyApp.xcodeproj")
build_app(scheme: "MyApp")
upload_to_app_store(
skip_screenshots: true,
skip_metadata: true
)
end
end
# android/fastlane/Fastfile
default_platform(:android)
platform :android do
desc "Deploy to Play Store internal"
lane :beta do
gradle(task: "clean bundleRelease")
upload_to_play_store(
track: "internal",
aab: "app/build/outputs/bundle/release/app-release.aab"
)
end
end26. How do you implement OTA updates?
Over-The-Air updates allow deploying JavaScript code without going through stores.
// With Expo Updates
import * as Updates from 'expo-updates'
async function checkForUpdates() {
if (__DEV__) return // Not in development
try {
const update = await Updates.checkForUpdateAsync()
if (update.isAvailable) {
// Download update
await Updates.fetchUpdateAsync()
// Ask user to restart
Alert.alert(
'Update available',
'A new version is available. Restart now?',
[
{ text: 'Later', style: 'cancel' },
{
text: 'Restart',
onPress: () => Updates.reloadAsync()
}
]
)
}
} catch (error) {
console.error('Update check error:', error)
}
}
// Automatic check on startup
function App() {
useEffect(() => {
checkForUpdates()
}, [])
return <AppNavigator />
}
// eas.json configuration for update channels
{
"build": {
"production": {
"channel": "production"
},
"preview": {
"channel": "preview"
}
}
}
// Command to publish an update
// eas update --branch production --message "Bug fix"
// With CodePush (Microsoft)
import codePush from 'react-native-code-push'
const codePushOptions = {
checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
installMode: codePush.InstallMode.ON_NEXT_RESTART
}
function App() {
return <AppNavigator />
}
export default codePush(codePushOptions)(App)Advanced Questions
27. How do you optimize app startup time?
Startup time is critical for user experience.
// android/app/build.gradle
project.ext.react = [
enableHermes: true
]
// ios/Podfile
:hermes_enabled => true
// 2. Lazy loading screens
const HeavyScreen = React.lazy(() => import('./HeavyScreen'))
// 3. Defer non-critical initializations
import { InteractionManager } from 'react-native'
function App() {
useEffect(() => {
// Execute after first render
InteractionManager.runAfterInteractions(() => {
// Initialize analytics
Analytics.init()
// Prefetch data
prefetchCriticalData()
})
}, [])
return <AppNavigator />
}
// 4. Optimize splash screen
import * as SplashScreen from 'expo-splash-screen'
// Prevent automatic hiding
SplashScreen.preventAutoHideAsync()
function App() {
const [appIsReady, setAppIsReady] = useState(false)
useEffect(() => {
async function prepare() {
try {
// Load critical resources
await Font.loadAsync(customFonts)
await Image.prefetch(criticalImages)
// Restore authentication
await restoreAuth()
} catch (e) {
console.warn(e)
} finally {
setAppIsReady(true)
}
}
prepare()
}, [])
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
// Hide splash screen
await SplashScreen.hideAsync()
}
}, [appIsReady])
if (!appIsReady) return null
return (
<View style={{ flex: 1 }} onLayout={onLayoutRootView}>
<AppNavigator />
</View>
)
}
// 5. Inline requires to defer imports
// metro.config.js
module.exports = {
transformer: {
inlineRequires: true
}
}
// Manual usage
function loadHeavyModule() {
const HeavyModule = require('./HeavyModule').default
return HeavyModule
}28. How do you handle security in a React Native app?
Mobile security requires multiple layers of protection.
import * as SecureStore from 'expo-secure-store'
// or
import * as Keychain from 'react-native-keychain'
async function saveToken(token) {
await SecureStore.setItemAsync('auth_token', token)
}
async function getToken() {
return await SecureStore.getItemAsync('auth_token')
}
// 2. Certificate pinning for network calls
// android/app/src/main/res/xml/network_security_config.xml
/*
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.myapp.com</domain>
<pin-set>
<pin digest="SHA-256">AAAAAA...</pin>
<pin digest="SHA-256">BBBBBB...</pin>
</pin-set>
</domain-config>
</network-security-config>
*/
// 3. Jailbreak/root detection
import JailMonkey from 'jail-monkey'
function SecurityCheck() {
useEffect(() => {
if (JailMonkey.isJailBroken()) {
Alert.alert(
'Insecure device',
'This application cannot run on a rooted/jailbroken device'
)
}
}, [])
}
// 4. Code obfuscation
// metro.config.js (for Hermes)
module.exports = {
transformer: {
minifierConfig: {
mangle: true,
output: {
ascii_only: true
}
}
}
}
// 5. Screenshot/recording protection
import { usePreventScreenCapture } from 'expo-screen-capture'
function SensitiveScreen() {
usePreventScreenCapture() // iOS only
return <View>{/* Sensitive data */}</View>
}
// 6. Session timeout
function useSessionTimeout(timeoutMs = 5 * 60 * 1000) {
const lastActivity = useRef(Date.now())
const { logout } = useAuth()
useEffect(() => {
const subscription = AppState.addEventListener('change', (state) => {
if (state === 'active') {
const elapsed = Date.now() - lastActivity.current
if (elapsed > timeoutMs) {
logout()
}
} else {
lastActivity.current = Date.now()
}
})
return () => subscription.remove()
}, [timeoutMs, logout])
}29. How do you implement accessibility?
Accessibility is essential to make the app usable by everyone.
import { AccessibilityInfo } from 'react-native'
// 1. Basic accessibility props
function AccessibleButton({ label, onPress, disabled }) {
return (
<TouchableOpacity
onPress={onPress}
disabled={disabled}
accessible={true}
accessibilityLabel={label}
accessibilityRole="button"
accessibilityState={{ disabled }}
accessibilityHint={`Tap to ${label.toLowerCase()}`}
>
<Text>{label}</Text>
</TouchableOpacity>
)
}
// 2. Group elements for screen readers
function ProductCard({ product }) {
return (
<View
accessible={true}
accessibilityLabel={`${product.name}, ${product.price} dollars`}
>
<Image
source={{ uri: product.image }}
accessibilityIgnoresInvertColors={true}
/>
<Text>{product.name}</Text>
<Text>${product.price}</Text>
</View>
)
}
// 3. Announce dynamic changes
function NotificationBadge({ count }) {
useEffect(() => {
if (count > 0) {
AccessibilityInfo.announceForAccessibility(
`${count} new notification${count > 1 ? 's' : ''}`
)
}
}, [count])
return (
<View accessibilityLabel={`${count} notifications`}>
<Text>{count}</Text>
</View>
)
}
// 4. Detect accessibility preferences
function useAccessibilityPreferences() {
const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false)
const [isReduceMotionEnabled, setIsReduceMotionEnabled] = useState(false)
useEffect(() => {
AccessibilityInfo.isScreenReaderEnabled().then(setIsScreenReaderEnabled)
AccessibilityInfo.isReduceMotionEnabled().then(setIsReduceMotionEnabled)
const screenReaderListener = AccessibilityInfo.addEventListener(
'screenReaderChanged',
setIsScreenReaderEnabled
)
const reduceMotionListener = AccessibilityInfo.addEventListener(
'reduceMotionChanged',
setIsReduceMotionEnabled
)
return () => {
screenReaderListener.remove()
reduceMotionListener.remove()
}
}, [])
return { isScreenReaderEnabled, isReduceMotionEnabled }
}
// 5. Adapt animations based on preferences
function AnimatedComponent() {
const { isReduceMotionEnabled } = useAccessibilityPreferences()
const animation = useSharedValue(0)
useEffect(() => {
animation.value = withTiming(1, {
duration: isReduceMotionEnabled ? 0 : 300
})
}, [isReduceMotionEnabled])
return <Animated.View style={animatedStyle} />
}30. How do you structure a large-scale React Native project?
A clear architecture facilitates maintenance and scalability.
// Recommended structure
src/
├── app/ # App configuration
│ ├── App.tsx
│ ├── Navigation.tsx
│ └── Providers.tsx
│
├── features/ # Feature modules
│ ├── auth/
│ │ ├── screens/
│ │ │ ├── LoginScreen.tsx
│ │ │ └── RegisterScreen.tsx
│ │ ├── components/
│ │ │ └── AuthForm.tsx
│ │ ├── hooks/
│ │ │ └── useAuth.ts
│ │ ├── services/
│ │ │ └── authService.ts
│ │ └── index.ts # Public export
│ │
│ ├── products/
│ │ ├── screens/
│ │ ├── components/
│ │ ├── hooks/
│ │ └── services/
│ │
│ └── cart/
│ └── ...
│
├── shared/ # Shared code
│ ├── components/
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ └── Card.tsx
│ ├── hooks/
│ │ ├── useDebounce.ts
│ │ └── useNetworkStatus.ts
│ ├── utils/
│ │ ├── format.ts
│ │ └── validation.ts
│ └── types/
│ └── index.ts
│
├── services/ # Global services
│ ├── api/
│ │ ├── client.ts
│ │ └── interceptors.ts
│ ├── storage/
│ │ └── secureStorage.ts
│ └── analytics/
│ └── analytics.ts
│
├── store/ # Global state
│ ├── slices/
│ └── index.ts
│
└── theme/ # Design system
├── colors.ts
├── typography.ts
├── spacing.ts
└── index.ts// Example feature module organization
// features/products/index.ts
export { ProductListScreen } from './screens/ProductListScreen'
export { ProductDetailScreen } from './screens/ProductDetailScreen'
export { useProducts } from './hooks/useProducts'
export { ProductCard } from './components/ProductCard'
// features/products/hooks/useProducts.ts
import { useQuery } from '@tanstack/react-query'
import { productService } from '../services/productService'
export function useProducts(categoryId?: string) {
return useQuery({
queryKey: ['products', categoryId],
queryFn: () => productService.getProducts(categoryId)
})
}
// features/products/services/productService.ts
import { apiClient } from '@/services/api/client'
import { Product } from '../types'
export const productService = {
async getProducts(categoryId?: string): Promise<Product[]> {
const params = categoryId ? { category: categoryId } : {}
const response = await apiClient.get('/products', { params })
return response.data
},
async getProduct(id: string): Promise<Product> {
const response = await apiClient.get(`/products/${id}`)
return response.data
}
}
// Import alias configuration
// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
[
'module-resolver',
{
alias: {
'@': './src',
'@features': './src/features',
'@shared': './src/shared',
'@services': './src/services'
}
}
]
]
}Conclusion
These 30 questions cover the essential React Native knowledge expected in interviews. Key points to master:
- ✅ Architecture: Understand the bridge, JSI, and new architecture
- ✅ Components: Navigation, performant lists, animations
- ✅ State and data: Zustand/Redux, React Query, local storage
- ✅ Performance: FlatList optimization, memoization, profiling
- ✅ Mobile specifics: Permissions, notifications, deep links
- ✅ Testing: Jest, Testing Library, Detox
- ✅ Deployment: EAS Build, stores, OTA updates
- ✅ Security and accessibility: Secure storage, WCAG compliance
React Native interview preparation requires understanding mobile-specific aspects beyond React web knowledge. Practicing with real projects and testing on actual devices consolidates these concepts.
Start practicing!
Test your knowledge with our interview simulators and technical tests.
Tags
Share
Related articles

React Native: Building a Complete Mobile App in 2026
Complete guide to developing iOS and Android mobile applications with React Native. From setup to deployment, all the fundamentals to get started.

React Native vs Flutter: Complete 2026 Comparison
In-depth React Native vs Flutter comparison for 2026: performance, architecture, DX, costs. Guide to choosing the right cross-platform framework.

Spring Cloud Gateway Interview: Routing, Filters and Load Balancing
Master Spring Cloud Gateway for technical interviews: 12 questions covering routing, filters, load balancing and API Gateway patterns with code examples.