Master advanced React Native patterns and production-ready development practices.
Master advanced React Native patterns and production-ready development practices.
Implement code splitting and lazy loading to improve app performance and reduce initial bundle size
Content by: Pratik Keshvala
React Native Developer
Split your React Native app into smaller chunks and load them on-demand to improve startup time and reduce memory usage.
Implement lazy loading for screens, components, and features to optimize app performance and user experience.
// utils/lazyLoader.js
import { lazy, Suspense } from 'react';
import { View, ActivityIndicator, Text } from 'react-native';
// Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'));
const MapComponent = lazy(() => import('./MapComponent'));
const VideoPlayer = lazy(() => import('./VideoPlayer'));
// Loading component
const LoadingSpinner = ({ message = 'Loading...' }) => (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#2196F3" />
<Text style={styles.loadingText}>{message}</Text>
</View>
);
// Lazy wrapper component
const LazyWrapper = ({ children, fallback = <LoadingSpinner /> }) => (
<Suspense fallback={fallback}>
{children}
</Suspense>
);
// Route-based code splitting
const routes = {
Home: lazy(() => import('./screens/HomeScreen')),
Profile: lazy(() => import('./screens/ProfileScreen')),
Settings: lazy(() => import('./screens/SettingsScreen')),
Charts: lazy(() => import('./screens/ChartsScreen')),
Maps: lazy(() => import('./screens/MapsScreen')),
};
// Dynamic route loader
const loadRoute = async (routeName) => {
try {
const route = routes[routeName];
if (route) {
return await route();
}
throw new Error(`Route ${routeName} not found`);
} catch (error) {
console.error('Error loading route:', error);
return null;
}
};
// Feature-based code splitting
const features = {
analytics: lazy(() => import('./features/analytics')),
notifications: lazy(() => import('./features/notifications')),
payments: lazy(() => import('./features/payments')),
social: lazy(() => import('./features/social')),
};
// Conditional feature loading
const loadFeature = async (featureName, condition) => {
if (condition) {
try {
const feature = features[featureName];
if (feature) {
return await feature();
}
} catch (error) {
console.error(`Error loading feature ${featureName}:`, error);
}
}
return null;
};
// Performance monitoring for lazy loading
const withPerformanceMonitoring = (Component, componentName) => {
return (props) => {
const startTime = Date.now();
React.useEffect(() => {
const loadTime = Date.now() - startTime;
console.log(`${componentName} loaded in ${loadTime}ms`);
// Send to analytics
analytics().logEvent('component_load_time', {
component: componentName,
loadTime,
});
}, []);
return <Component {...props} />;
};
};
const styles = {
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
loadingText: {
marginTop: 10,
fontSize: 16,
color: '#666',
},
};
export { LazyWrapper, loadRoute, loadFeature, withPerformanceMonitoring };
// Usage example
const App = () => {
const [currentRoute, setCurrentRoute] = useState('Home');
return (
<LazyWrapper>
{React.createElement(routes[currentRoute])}
</LazyWrapper>
);
};Test your understanding of this topic:
Implement offline support and data synchronization for React Native applications
Content by: Pratik Keshvala
React Native Developer
Use AsyncStorage, SQLite, or Realm to store data locally and provide offline functionality to users.
Implement data synchronization strategies to sync local data with server when connectivity is restored.
// services/offlineManager.js
import AsyncStorage from '@react-native-async-storage/async-storage';
import NetInfo from '@react-native-community/netinfo';
class OfflineManager {
constructor() {
this.isOnline = true;
this.pendingActions = [];
this.syncQueue = [];
this.setupNetworkListener();
}
setupNetworkListener() {
NetInfo.addEventListener(state => {
const wasOffline = !this.isOnline;
this.isOnline = state.isConnected;
if (wasOffline && this.isOnline) {
this.syncPendingData();
}
});
}
// Store data locally
async storeData(key, data) {
try {
const jsonData = JSON.stringify({
data,
timestamp: Date.now(),
synced: false,
});
await AsyncStorage.setItem(key, jsonData);
if (this.isOnline) {
this.syncToServer(key, data);
} else {
this.addToSyncQueue(key, data);
}
} catch (error) {
console.error('Error storing data:', error);
}
}
// Retrieve data from local storage
async getData(key) {
try {
const jsonData = await AsyncStorage.getItem(key);
if (jsonData) {
const parsed = JSON.parse(jsonData);
return parsed.data;
}
return null;
} catch (error) {
console.error('Error retrieving data:', error);
return null;
}
}
// Add to sync queue when offline
addToSyncQueue(key, data) {
this.syncQueue.push({ key, data, timestamp: Date.now() });
}
// Sync pending data when online
async syncPendingData() {
console.log('Syncing pending data...');
for (const item of this.syncQueue) {
try {
await this.syncToServer(item.key, item.data);
await this.markAsSynced(item.key);
} catch (error) {
console.error('Error syncing item:', error);
}
}
this.syncQueue = [];
}
// Sync data to server
async syncToServer(key, data) {
try {
const response = await fetch(`https://api.yourapp.com/sync/${key}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (response.ok) {
await this.markAsSynced(key);
} else {
throw new Error('Sync failed');
}
} catch (error) {
console.error('Error syncing to server:', error);
throw error;
}
}
// Mark data as synced
async markAsSynced(key) {
try {
const jsonData = await AsyncStorage.getItem(key);
if (jsonData) {
const parsed = JSON.parse(jsonData);
parsed.synced = true;
await AsyncStorage.setItem(key, JSON.stringify(parsed));
}
} catch (error) {
console.error('Error marking as synced:', error);
}
}
// Get offline status
isOffline() {
return !this.isOnline;
}
// Get pending sync count
getPendingSyncCount() {
return this.syncQueue.length;
}
}
// Offline-aware API service
class OfflineAPI {
constructor() {
this.offlineManager = new OfflineManager();
}
async get(endpoint) {
const cacheKey = `cache_${endpoint}`;
if (this.offlineManager.isOffline()) {
return await this.offlineManager.getData(cacheKey);
}
try {
const response = await fetch(`https://api.yourapp.com${endpoint}`);
const data = await response.json();
// Cache the data
await this.offlineManager.storeData(cacheKey, data);
return data;
} catch (error) {
// Return cached data if available
const cachedData = await this.offlineManager.getData(cacheKey);
if (cachedData) {
return cachedData;
}
throw error;
}
}
async post(endpoint, data) {
if (this.offlineManager.isOffline()) {
// Store for later sync
await this.offlineManager.storeData(`pending_${Date.now()}`, {
endpoint,
method: 'POST',
data,
});
return { success: true, offline: true };
}
try {
const response = await fetch(`https://api.yourapp.com${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
return await response.json();
} catch (error) {
// Store for later sync
await this.offlineManager.storeData(`pending_${Date.now()}`, {
endpoint,
method: 'POST',
data,
});
throw error;
}
}
}
// Offline indicator component
const OfflineIndicator = () => {
const [isOffline, setIsOffline] = useState(false);
const [pendingSync, setPendingSync] = useState(0);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsOffline(!state.isConnected);
});
return unsubscribe;
}, []);
if (!isOffline) return null;
return (
<View style={styles.offlineIndicator}>
<Text style={styles.offlineText}>
You're offline. Changes will sync when you're back online.
</Text>
{pendingSync > 0 && (
<Text style={styles.syncText}>
{pendingSync} items pending sync
</Text>
)}
</View>
);
};
const styles = {
offlineIndicator: {
backgroundColor: '#ff9800',
padding: 10,
alignItems: 'center',
},
offlineText: {
color: 'white',
fontWeight: 'bold',
},
syncText: {
color: 'white',
fontSize: 12,
marginTop: 2,
},
};
export { OfflineManager, OfflineAPI, OfflineIndicator };Test your understanding of this topic:
Implement deep linking to allow users to navigate directly to specific screens in your React Native app
Content by: Pratik Keshvala
React Native Developer
Configure deep linking for both iOS and Android platforms with proper URL schemes and universal links.
Handle deep link navigation and route users to appropriate screens based on URL parameters.
// services/deepLinkManager.js
import { Linking } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
class DeepLinkManager {
constructor() {
this.navigationRef = null;
this.initialUrl = null;
this.setupLinking();
}
setNavigationRef(ref) {
this.navigationRef = ref;
}
setupLinking() {
// Handle initial URL when app is opened
Linking.getInitialURL().then(url => {
if (url) {
this.initialUrl = url;
this.handleDeepLink(url);
}
});
// Handle URL when app is already running
Linking.addEventListener('url', ({ url }) => {
this.handleDeepLink(url);
});
}
handleDeepLink(url) {
console.log('Deep link received:', url);
const parsedUrl = this.parseUrl(url);
if (parsedUrl) {
this.navigateToScreen(parsedUrl);
}
}
parseUrl(url) {
try {
const urlObj = new URL(url);
const path = urlObj.pathname;
const params = Object.fromEntries(urlObj.searchParams);
return {
path,
params,
scheme: urlObj.protocol.replace(':', ''),
host: urlObj.host,
};
} catch (error) {
console.error('Error parsing URL:', error);
return null;
}
}
navigateToScreen(parsedUrl) {
if (!this.navigationRef) {
console.log('Navigation ref not ready, storing for later');
return;
}
const { path, params } = parsedUrl;
switch (path) {
case '/profile':
this.navigationRef.navigate('Profile', { userId: params.id });
break;
case '/product':
this.navigationRef.navigate('Product', {
productId: params.id,
category: params.category
});
break;
case '/order':
this.navigationRef.navigate('Order', { orderId: params.id });
break;
case '/search':
this.navigationRef.navigate('Search', { query: params.q });
break;
case '/notification':
this.navigationRef.navigate('Notification', {
type: params.type,
id: params.id
});
break;
default:
console.log('Unknown deep link path:', path);
}
}
// Generate deep links
generateDeepLink(screen, params = {}) {
const baseUrl = 'yourapp://';
const queryString = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join('&');
return `${baseUrl}${screen}${queryString ? '?' + queryString : ''}`;
}
// Share deep link
async shareDeepLink(screen, params = {}) {
const deepLink = this.generateDeepLink(screen, params);
try {
await Share.share({
message: `Check this out: ${deepLink}`,
url: deepLink,
});
} catch (error) {
console.error('Error sharing deep link:', error);
}
}
}
// Deep link configuration
const linkingConfig = {
prefixes: ['yourapp://', 'https://yourapp.com'],
config: {
screens: {
Home: '',
Profile: 'profile/:userId?',
Product: 'product/:productId',
Order: 'order/:orderId',
Search: 'search',
Notification: 'notification/:type/:id?',
Settings: 'settings',
},
},
};
// Deep link hook
const useDeepLink = () => {
const [deepLinkData, setDeepLinkData] = useState(null);
const deepLinkManager = useRef(new DeepLinkManager());
useEffect(() => {
const handleDeepLink = (url) => {
const parsed = deepLinkManager.current.parseUrl(url);
setDeepLinkData(parsed);
};
const unsubscribe = Linking.addEventListener('url', ({ url }) => {
handleDeepLink(url);
});
return unsubscribe;
}, []);
const generateLink = (screen, params) => {
return deepLinkManager.current.generateDeepLink(screen, params);
};
const shareLink = (screen, params) => {
return deepLinkManager.current.shareDeepLink(screen, params);
};
return {
deepLinkData,
generateLink,
shareLink,
};
};
// Deep link button component
const DeepLinkButton = ({ screen, params, children, ...props }) => {
const { generateLink, shareLink } = useDeepLink();
const handlePress = () => {
const link = generateLink(screen, params);
console.log('Generated deep link:', link);
// You can copy to clipboard or share
};
return (
<TouchableOpacity onPress={handlePress} {...props}>
{children}
</TouchableOpacity>
);
};
// Android configuration
// android/app/src/main/AndroidManifest.xml
const androidManifestConfig = `
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourapp" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="yourapp.com" />
</intent-filter>
</activity>
`;
// iOS configuration
// ios/YourApp/Info.plist
const iosInfoPlistConfig = `
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>yourapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>yourapp</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>yourapp.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>https</string>
</array>
</dict>
</array>
`;
export { DeepLinkManager, linkingConfig, useDeepLink, DeepLinkButton };Test your understanding of this topic:
Learn production-ready development practices and deployment strategies for React Native applications
Content by: Pratik Keshvala
React Native Developer
Follow a comprehensive checklist for preparing React Native apps for production deployment including security, performance, and monitoring.
Implement crash reporting, analytics, and performance monitoring for production React Native applications.
// services/monitoringService.js
import crashlytics from '@react-native-firebase/crashlytics';
import analytics from '@react-native-firebase/analytics';
import perf from '@react-native-firebase/perf';
class MonitoringService {
constructor() {
this.isInitialized = false;
this.init();
}
async init() {
try {
// Initialize Firebase services
await this.setupCrashlytics();
await this.setupAnalytics();
await this.setupPerformance();
this.isInitialized = true;
console.log('Monitoring service initialized');
} catch (error) {
console.error('Error initializing monitoring:', error);
}
}
// Crash Reporting
async setupCrashlytics() {
// Enable crash collection
await crashlytics().setCrashlyticsCollectionEnabled(true);
// Set user identifier
crashlytics().setUserId('user123');
// Set custom attributes
crashlytics().setAttributes({
app_version: '1.0.0',
platform: Platform.OS,
build_number: '1',
});
}
// Analytics
async setupAnalytics() {
// Set user properties
await analytics().setUserProperties({
user_type: 'premium',
subscription_status: 'active',
});
}
// Performance Monitoring
async setupPerformance() {
// Start app startup trace
const trace = await perf().startTrace('app_startup');
// End trace when app is ready
setTimeout(() => {
trace.stop();
}, 2000);
}
// Log custom events
async logEvent(eventName, parameters = {}) {
if (!this.isInitialized) return;
try {
await analytics().logEvent(eventName, {
timestamp: Date.now(),
platform: Platform.OS,
...parameters,
});
} catch (error) {
console.error('Error logging event:', error);
}
}
// Log errors
async logError(error, context = {}) {
if (!this.isInitialized) return;
try {
crashlytics().recordError(error);
crashlytics().setAttributes(context);
} catch (err) {
console.error('Error logging to crashlytics:', err);
}
}
// Performance monitoring
async startTrace(traceName) {
if (!this.isInitialized) return null;
try {
return await perf().startTrace(traceName);
} catch (error) {
console.error('Error starting trace:', error);
return null;
}
}
// Network monitoring
async startNetworkTrace(url, method = 'GET') {
if (!this.isInitialized) return null;
try {
return await perf().startTrace(`network_${method.toLowerCase()}_${url.replace(/[^a-zA-Z0-9]/g, '_')}`);
} catch (error) {
console.error('Error starting network trace:', error);
return null;
}
}
}
// Error Boundary Component
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Log error to monitoring service
monitoringService.logError(error, {
componentStack: errorInfo.componentStack,
errorBoundary: this.constructor.name,
});
}
render() {
if (this.state.hasError) {
return (
<View style={styles.errorContainer}>
<Text style={styles.errorTitle}>Something went wrong</Text>
<Text style={styles.errorMessage}>
We're sorry, but something unexpected happened.
</Text>
<TouchableOpacity
style={styles.retryButton}
onPress={() => this.setState({ hasError: false, error: null })}
>
<Text style={styles.retryText}>Try Again</Text>
</TouchableOpacity>
</View>
);
}
return this.props.children;
}
}
// Performance monitoring hook
const usePerformanceMonitoring = () => {
const [metrics, setMetrics] = useState({});
const trackScreenView = async (screenName) => {
await monitoringService.logEvent('screen_view', {
screen_name: screenName,
screen_class: screenName,
});
};
const trackUserAction = async (action, parameters = {}) => {
await monitoringService.logEvent('user_action', {
action,
...parameters,
});
};
const trackApiCall = async (endpoint, method, duration, success) => {
await monitoringService.logEvent('api_call', {
endpoint,
method,
duration,
success,
});
};
return {
trackScreenView,
trackUserAction,
trackApiCall,
metrics,
};
};
// Production configuration
const productionConfig = {
// Environment
environment: 'production',
// API Configuration
api: {
baseUrl: 'https://api.yourapp.com',
timeout: 30000,
retryAttempts: 3,
},
// Analytics
analytics: {
enabled: true,
debugMode: false,
sampleRate: 1.0,
},
// Crash Reporting
crashlytics: {
enabled: true,
collectionEnabled: true,
},
// Performance
performance: {
enabled: true,
dataCollectionEnabled: true,
instrumentationEnabled: true,
},
// Security
security: {
enableSSLPinning: true,
enableCertificateTransparency: true,
enableAppTransportSecurity: true,
},
// Logging
logging: {
level: 'error',
enableConsoleLogs: false,
enableRemoteLogging: true,
},
};
const styles = {
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#f5f5f5',
},
errorTitle: {
fontSize: 24,
fontWeight: 'bold',
color: '#d32f2f',
marginBottom: 10,
},
errorMessage: {
fontSize: 16,
color: '#666',
textAlign: 'center',
marginBottom: 20,
},
retryButton: {
backgroundColor: '#2196F3',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
},
retryText: {
color: 'white',
fontWeight: 'bold',
},
};
// Initialize monitoring service
const monitoringService = new MonitoringService();
export {
MonitoringService,
ErrorBoundary,
usePerformanceMonitoring,
productionConfig
};Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Back to Course Overview