Skip to content

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),
  },
});

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;
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