Appearance
React Native Cheatsheet
Overview
React Native is a framework for building native mobile applications using React and JavaScript. It allows developers to create iOS and Android apps with a single codebase while maintaining native performance and look.
Installation
Prerequisites
bash
# Node.js (version 14 or newer)
node --version
npm --version
# Watchman (macOS)
brew install watchman
# Java Development Kit (JDK 11)
java -version
# Android Studio (for Android development)
# Xcode (for iOS development - macOS only)
React Native CLI
bash
# Install React Native CLI globally
npm install -g @react-native-community/cli
# Create new project
npx react-native init MyProject
cd MyProject
# Create project with specific version
npx react-native init MyProject --version 0.72.0
# Create project with TypeScript
npx react-native init MyProject --template react-native-template-typescript
Expo CLI (Alternative)
bash
# Install Expo CLI
npm install -g @expo/cli
# Create new Expo project
npx create-expo-app MyProject
cd MyProject
# Start development server
npx expo start
Project Structure
Basic Structure
MyProject/
├── android/
├── ios/
├── node_modules/
├── src/
│ ├── components/
│ ├── screens/
│ ├── navigation/
│ ├── services/
│ └── utils/
├── App.js
├── index.js
├── package.json
├── metro.config.js
└── babel.config.js
TypeScript Structure
MyProject/
├── android/
├── ios/
├── src/
│ ├── components/
│ │ └── Button/
│ │ ├── Button.tsx
│ │ ├── Button.styles.ts
│ │ └── index.ts
│ ├── screens/
│ ├── types/
│ ├── hooks/
│ └── utils/
├── App.tsx
├── index.js
├── tsconfig.json
└── package.json
Basic Components
Core Components
jsx
import React from 'react';
import {
View,
Text,
Image,
ScrollView,
TextInput,
TouchableOpacity,
FlatList,
StyleSheet,
} from 'react-native';
const MyComponent = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello React Native</Text>
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={styles.image}
/>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Press Me</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
image: {
width: 200,
height: 200,
marginBottom: 20,
},
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default MyComponent;
Functional Components with Hooks
jsx
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, Alert } from 'react-native';
const UserForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
// Component did mount
console.log('Component mounted');
return () => {
// Component will unmount
console.log('Component will unmount');
};
}, []);
const handleSubmit = async () => {
if (!name || !email) {
Alert.alert('Error', 'Please fill all fields');
return;
}
setLoading(true);
try {
// API call
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email }),
});
if (response.ok) {
Alert.alert('Success', 'User created successfully');
setName('');
setEmail('');
}
} catch (error) {
Alert.alert('Error', 'Failed to create user');
} finally {
setLoading(false);
}
};
return (
<View style={{ padding: 20 }}>
<TextInput
placeholder="Name"
value={name}
onChangeText={setName}
style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
/>
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
/>
<Button
title={loading ? "Creating..." : "Create User"}
onPress={handleSubmit}
disabled={loading}
/>
</View>
);
};
export default UserForm;
Styling
StyleSheet
jsx
import { StyleSheet, Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
},
header: {
height: 60,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
paddingTop: 20,
},
title: {
fontSize: 18,
fontWeight: 'bold',
color: 'white',
},
content: {
flex: 1,
padding: 20,
},
card: {
backgroundColor: 'white',
borderRadius: 8,
padding: 16,
marginVertical: 8,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
button: {
backgroundColor: '#007AFF',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 6,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
input: {
borderWidth: 1,
borderColor: '#ddd',
padding: 12,
borderRadius: 6,
fontSize: 16,
marginBottom: 12,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
centered: {
justifyContent: 'center',
alignItems: 'center',
},
});
export default styles;
Responsive Design
jsx
import { Dimensions, PixelRatio } from 'react-native';
const { width, height } = Dimensions.get('window');
// Responsive dimensions
const responsiveWidth = (percentage) => {
return (percentage * width) / 100;
};
const responsiveHeight = (percentage) => {
return (percentage * height) / 100;
};
// Font scaling
const responsiveFontSize = (size) => {
const scale = width / 320;
const newSize = size * scale;
return Math.max(12, PixelRatio.roundToNearestPixel(newSize));
};
// Usage
const styles = StyleSheet.create({
container: {
width: responsiveWidth(90),
height: responsiveHeight(50),
},
title: {
fontSize: responsiveFontSize(18),
},
});
Navigation
React Navigation Setup
bash
# Install React Navigation
npm install @react-navigation/native
# Install dependencies
npm install react-native-screens react-native-safe-area-context
# For iOS
cd ios && pod install
# Install navigators
npm install @react-navigation/stack
npm install @react-navigation/bottom-tabs
npm install @react-navigation/drawer
Stack Navigator
jsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: {
backgroundColor: '#007AFF',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'My Home' }}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={({ route }) => ({ title: route.params.title })}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Tab Navigator
jsx
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
import HomeScreen from './screens/HomeScreen';
import ProfileScreen from './screens/ProfileScreen';
import SettingsScreen from './screens/SettingsScreen';
const Tab = createBottomTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'settings' : 'settings-outline';
}
return <Icon name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: '#007AFF',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
};
export default TabNavigator;
Navigation Actions
jsx
import React from 'react';
import { View, Button } from 'react-native';
import { useNavigation } from '@react-navigation/native';
const HomeScreen = () => {
const navigation = useNavigation();
const navigateToDetails = () => {
navigation.navigate('Details', {
itemId: 86,
title: 'Item Details',
});
};
const goBack = () => {
navigation.goBack();
};
const resetToHome = () => {
navigation.reset({
index: 0,
routes: [{ name: 'Home' }],
});
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Button title="Go to Details" onPress={navigateToDetails} />
<Button title="Go Back" onPress={goBack} />
<Button title="Reset to Home" onPress={resetToHome} />
</View>
);
};
export default HomeScreen;
State Management
Context API
jsx
import React, { createContext, useContext, useReducer } from 'react';
// Create context
const AppContext = createContext();
// Reducer
const appReducer = (state, action) => {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload };
case 'SET_LOADING':
return { ...state, loading: action.payload };
case 'SET_ERROR':
return { ...state, error: action.payload };
default:
return state;
}
};
// Provider component
export const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(appReducer, {
user: null,
loading: false,
error: null,
});
const setUser = (user) => {
dispatch({ type: 'SET_USER', payload: user });
};
const setLoading = (loading) => {
dispatch({ type: 'SET_LOADING', payload: loading });
};
const setError = (error) => {
dispatch({ type: 'SET_ERROR', payload: error });
};
return (
<AppContext.Provider value={{
...state,
setUser,
setLoading,
setError,
}}>
{children}
</AppContext.Provider>
);
};
// Custom hook
export const useApp = () => {
const context = useContext(AppContext);
if (!context) {
throw new Error('useApp must be used within AppProvider');
}
return context;
};
// Usage in component
const MyComponent = () => {
const { user, loading, setUser, setLoading } = useApp();
// Component logic
};
Redux Setup
bash
# Install Redux
npm install @reduxjs/toolkit react-redux
jsx
// store/store.js
import { configureStore } from '@reduxjs/toolkit';
import userSlice from './userSlice';
import postsSlice from './postsSlice';
export const store = configureStore({
reducer: {
user: userSlice,
posts: postsSlice,
},
});
// store/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
);
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
loading: false,
error: null,
},
reducers: {
setUser: (state, action) => {
state.data = action.payload;
},
clearUser: (state) => {
state.data = null;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});
export const { setUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store/store';
import MainApp from './MainApp';
const App = () => {
return (
<Provider store={store}>
<MainApp />
</Provider>
);
};
export default App;
API Integration
Fetch API
jsx
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
const PostsList = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
setLoading(true);
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Failed to fetch posts');
}
const data = await response.json();
setPosts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const renderPost = ({ item }) => (
<View style={{ padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
<Text style={{ fontSize: 16, fontWeight: 'bold' }}>{item.title}</Text>
<Text style={{ marginTop: 8 }}>{item.body}</Text>
</View>
);
if (loading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#007AFF" />
</View>
);
}
if (error) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'red' }}>Error: {error}</Text>
</View>
);
}
return (
<FlatList
data={posts}
renderItem={renderPost}
keyExtractor={(item) => item.id.toString()}
refreshing={loading}
onRefresh={fetchPosts}
/>
);
};
export default PostsList;
Axios Integration
bash
npm install axios
jsx
import axios from 'axios';
// API configuration
const API_BASE_URL = 'https://api.example.com';
const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor
apiClient.interceptors.request.use(
(config) => {
// Add auth token
const token = getAuthToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Response interceptor
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Handle unauthorized
logout();
}
return Promise.reject(error);
}
);
// API methods
export const api = {
get: (url, config) => apiClient.get(url, config),
post: (url, data, config) => apiClient.post(url, data, config),
put: (url, data, config) => apiClient.put(url, data, config),
delete: (url, config) => apiClient.delete(url, config),
};
// Usage
const fetchUserData = async (userId) => {
try {
const response = await api.get(`/users/${userId}`);
return response.data;
} catch (error) {
throw new Error(error.response?.data?.message || 'Failed to fetch user');
}
};
Native Modules
Accessing Device Features
jsx
import React, { useState } from 'react';
import { View, Button, Alert, Platform } from 'react-native';
import {
Camera,
MediaLibrary,
Location,
Permissions,
Contacts,
Calendar,
} from 'expo';
const DeviceFeatures = () => {
const [location, setLocation] = useState(null);
const requestCameraPermission = async () => {
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'App needs camera permission to take photos',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
};
const takePicture = async () => {
const hasPermission = await requestCameraPermission();
if (!hasPermission) {
Alert.alert('Permission denied');
return;
}
// Camera logic here
};
const getCurrentLocation = async () => {
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
Alert.alert('Permission denied');
return;
}
const location = await Location.getCurrentPositionAsync({});
setLocation(location);
} catch (error) {
Alert.alert('Error', 'Failed to get location');
}
};
return (
<View style={{ padding: 20 }}>
<Button title="Take Picture" onPress={takePicture} />
<Button title="Get Location" onPress={getCurrentLocation} />
</View>
);
};
export default DeviceFeatures;
Performance Optimization
FlatList Optimization
jsx
import React, { memo } from 'react';
import { FlatList, View, Text } from 'react-native';
const ListItem = memo(({ item }) => (
<View style={{ padding: 16, borderBottomWidth: 1 }}>
<Text>{item.title}</Text>
</View>
));
const OptimizedList = ({ data }) => {
const renderItem = ({ item }) => <ListItem item={item} />;
const getItemLayout = (data, index) => ({
length: 60, // Fixed item height
offset: 60 * index,
index,
});
const keyExtractor = (item) => item.id.toString();
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
initialNumToRender={10}
windowSize={10}
/>
);
};
export default OptimizedList;
Image Optimization
jsx
import React from 'react';
import { Image } from 'react-native';
import FastImage from 'react-native-fast-image';
const OptimizedImage = ({ source, style }) => {
return (
<FastImage
style={style}
source={{
uri: source,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
resizeMode={FastImage.resizeMode.cover}
/>
);
};
// Usage
<OptimizedImage
source="https://example.com/image.jpg"
style={{ width: 200, height: 200 }}
/>
Testing
Jest Testing
jsx
// __tests__/Button.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import Button from '../src/components/Button';
describe('Button Component', () => {
it('renders correctly', () => {
const { getByText } = render(
<Button title="Test Button" onPress={() => {}} />
);
expect(getByText('Test Button')).toBeTruthy();
});
it('calls onPress when pressed', () => {
const mockOnPress = jest.fn();
const { getByText } = render(
<Button title="Test Button" onPress={mockOnPress} />
);
fireEvent.press(getByText('Test Button'));
expect(mockOnPress).toHaveBeenCalledTimes(1);
});
it('is disabled when loading', () => {
const { getByText } = render(
<Button title="Test Button" onPress={() => {}} loading={true} />
);
const button = getByText('Test Button');
expect(button.props.accessibilityState.disabled).toBe(true);
});
});
E2E Testing with Detox
bash
# Install Detox
npm install --save-dev detox
# iOS setup
npm install --save-dev detox-cli
javascript
// e2e/firstTest.e2e.js
describe('App', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('should show welcome screen', async () => {
await expect(element(by.text('Welcome'))).toBeVisible();
});
it('should navigate to details screen', async () => {
await element(by.id('details-button')).tap();
await expect(element(by.text('Details'))).toBeVisible();
});
it('should add new item', async () => {
await element(by.id('add-button')).tap();
await element(by.id('title-input')).typeText('New Item');
await element(by.id('save-button')).tap();
await expect(element(by.text('New Item'))).toBeVisible();
});
});
Deployment
Android Build
bash
# Generate signed APK
cd android
./gradlew assembleRelease
# Generate AAB (recommended for Play Store)
./gradlew bundleRelease
# Install on device
npx react-native run-android --variant=release
iOS Build
bash
# Build for iOS
npx react-native run-ios --configuration Release
# Archive for App Store (in Xcode)
# Product -> Archive
Code Push (Over-the-air updates)
bash
# Install CodePush
npm install --save react-native-code-push
# Release update
appcenter codepush release-react MyApp-iOS ios
appcenter codepush release-react MyApp-Android android
Debugging
Debug Tools
jsx
// Enable debugging
import { enableScreens } from 'react-native-screens';
enableScreens();
// Flipper integration
import { logger } from 'flipper-plugin';
const MyComponent = () => {
logger.info('Component rendered');
return (
// Component JSX
);
};
// Performance monitoring
import { Performance } from 'react-native-performance';
Performance.mark('component-start');
// Component logic
Performance.mark('component-end');
Performance.measure('component-render', 'component-start', 'component-end');
Common Debug Commands
bash
# Enable debugging
npx react-native start --reset-cache
# Debug on device
adb reverse tcp:8081 tcp:8081 # Android
adb reverse tcp:9090 tcp:9090 # Flipper
# iOS Simulator
xcrun simctl openurl booted "http://localhost:8081/debugger-ui"
# Clear cache
npx react-native start --reset-cache
rm -rf node_modules && npm install
Resources
- Official Documentation: reactnative.dev
- React Navigation: reactnavigation.org
- Expo Documentation: docs.expo.dev
- Awesome React Native: github.com/jondot/awesome-react-native