Practice and reinforce the concepts from Lesson 17
Master app performance optimization by:
Time Limit: 10 minutes
Install Performance Tools:
npx expo install expo-performance
npm install react-native-performance
npm install @react-native-flipper/flipper-plugin-performance
Basic Performance Monitoring:
import { Performance } from 'react-native-performance';
import * as TaskManager from 'expo-task-manager';
// Performance monitoring class
class AppPerformanceMonitor {
constructor() {
this.metrics = new Map();
this.alerts = [];
this.isMonitoring = false;
}
startMonitoring() {
this.isMonitoring = true;
// Monitor JavaScript thread
this.monitorJSThread();
// Monitor memory usage
this.monitorMemoryUsage();
// Monitor network requests
this.monitorNetworkPerformance();
// Monitor navigation performance
this.monitorNavigationTiming();
}
monitorJSThread() {
// Track JavaScript thread blocking
const measureJSPerformance = () => {
const start = performance.now();
// Simulate work to measure thread responsiveness
setTimeout(() => {
const end = performance.now();
const jsThreadDelay = end - start;
if (jsThreadDelay > 16.67) { // 60fps = 16.67ms per frame
this.recordPerformanceIssue('js_thread_blocked', {
delay: jsThreadDelay,
severity: jsThreadDelay > 33 ? 'critical' : 'warning'
});
}
if (this.isMonitoring) {
setTimeout(measureJSPerformance, 1000); // Check every second
}
}, 0);
};
measureJSPerformance();
}
monitorMemoryUsage() {
const checkMemory = () => {
if (performance.memory) {
const memoryInfo = {
used: Math.round(performance.memory.usedJSHeapSize / 1048576), // MB
total: Math.round(performance.memory.totalJSHeapSize / 1048576), // MB
limit: Math.round(performance.memory.jsHeapSizeLimit / 1048576) // MB
};
// Alert if memory usage is high
const usagePercentage = (memoryInfo.used / memoryInfo.limit) * 100;
if (usagePercentage > 80) {
this.recordPerformanceIssue('high_memory_usage', {
usage: memoryInfo,
usagePercentage: usagePercentage.toFixed(1),
severity: usagePercentage > 90 ? 'critical' : 'warning'
});
}
this.updateMetric('memory_usage', memoryInfo);
}
if (this.isMonitoring) {
setTimeout(checkMemory, 5000); // Check every 5 seconds
}
};
checkMemory();
}
// Performance measurement utilities
startTimer(name) {
performance.mark(`${name}_start`);
}
endTimer(name, properties = {}) {
performance.mark(`${name}_end`);
performance.measure(name, `${name}_start`, `${name}_end`);
const measures = performance.getEntriesByName(name, 'measure');
if (measures.length > 0) {
const duration = measures[measures.length - 1].duration;
this.recordMetric(name, {
duration,
timestamp: new Date().toISOString(),
...properties
});
// Clean up marks
performance.clearMarks(`${name}_start`);
performance.clearMarks(`${name}_end`);
performance.clearMeasures(name);
return duration;
}
}
recordMetric(name, data) {
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
}
this.metrics.get(name).push(data);
// Keep only last 100 entries per metric
const metricData = this.metrics.get(name);
if (metricData.length > 100) {
metricData.shift();
}
}
recordPerformanceIssue(type, data) {
const alert = {
type,
data,
timestamp: new Date().toISOString(),
id: Date.now().toString()
};
this.alerts.push(alert);
// Send to analytics or logging service
console.warn('Performance Issue:', alert);
// Keep only last 50 alerts
if (this.alerts.length > 50) {
this.alerts.shift();
}
}
}
// Global performance monitor instance
export const performanceMonitor = new AppPerformanceMonitor();
✅ Checkpoint: Performance monitoring is running and capturing metrics!
Time Limit: 5 minutes
Create a hook to monitor component performance:
import React, { useEffect, useRef } from 'react';
// Hook for monitoring component performance
export const useComponentPerformance = (componentName) => {
const renderStartTime = useRef();
const mountStartTime = useRef();
// Track component mount time
useEffect(() => {
if (mountStartTime.current) {
const mountDuration = performance.now() - mountStartTime.current;
performanceMonitor.recordMetric('component_mount_time', {
component: componentName,
duration: mountDuration
});
}
}, [componentName]);
// Track render performance
const trackRender = (renderType = 'render') => {
renderStartTime.current = performance.now();
// Return cleanup function
return () => {
if (renderStartTime.current) {
const renderDuration = performance.now() - renderStartTime.current;
performanceMonitor.recordMetric('component_render_time', {
component: componentName,
renderType,
duration: renderDuration
});
}
};
};
// Initialize mount tracking
if (!mountStartTime.current) {
mountStartTime.current = performance.now();
}
return { trackRender };
};
// Usage example
const OptimizedComponent = ({ data }) => {
const { trackRender } = useComponentPerformance('OptimizedComponent');
useEffect(() => {
const cleanup = trackRender('effect');
// Your effect logic here
return cleanup;
}, [data]);
const handlePress = () => {
const cleanup = trackRender('interaction');
// Handle press logic
cleanup();
};
return (
<View>
<Text>Optimized Component</Text>
<TouchableOpacity onPress={handlePress}>
<Text>Test Interaction</Text>
</TouchableOpacity>
</View>
);
};
✅ Checkpoint: Component performance is being tracked automatically!
Build a high-performance fitness app with advanced optimizations:
import React, { useState, useEffect, useMemo, useCallback, memo } from 'react';
import { View, FlatList, Image, Text, TouchableOpacity, Dimensions } from 'react-native';
import { FlashList } from '@shopify/flash-list';
// High-performance workout list component
const WorkoutList = memo(({ workouts, onWorkoutSelect, searchQuery }) => {
const { trackRender } = useComponentPerformance('WorkoutList');
// Memoized filtered workouts to prevent unnecessary recalculations
const filteredWorkouts = useMemo(() => {
const cleanup = trackRender('filter_calculation');
const filtered = searchQuery
? workouts.filter(workout =>
workout.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
workout.category.toLowerCase().includes(searchQuery.toLowerCase())
)
: workouts;
cleanup();
return filtered;
}, [workouts, searchQuery]);
// Memoized render function to prevent recreating on every render
const renderWorkout = useCallback(({ item, index }) => (
<OptimizedWorkoutCard
workout={item}
onSelect={onWorkoutSelect}
index={index}
/>
), [onWorkoutSelect]);
// Optimized key extractor
const keyExtractor = useCallback((item) => item.id, []);
// Performance optimizations for FlatList
const listOptimizations = {
removeClippedSubviews: true, // Remove off-screen items from memory
maxToRenderPerBatch: 10, // Render 10 items per batch
windowSize: 10, // Keep 10 screens worth of items in memory
initialNumToRender: 10, // Render 10 items initially
updateCellsBatchingPeriod: 50, // Batch updates every 50ms
getItemLayout: (data, index) => ({
length: 120, // Fixed height for each item
offset: 120 * index,
index,
})
};
return (
<FlashList
data={filteredWorkouts}
renderItem={renderWorkout}
keyExtractor={keyExtractor}
estimatedItemSize={120}
{...listOptimizations}
/>
);
});
// Optimized workout card with image caching
const OptimizedWorkoutCard = memo(({ workout, onSelect, index }) => {
const [imageLoaded, setImageLoaded] = useState(false);
const [imageError, setImageError] = useState(false);
const handlePress = useCallback(() => {
performanceMonitor.startTimer('workout_selection');
onSelect(workout);
performanceMonitor.endTimer('workout_selection', {
workout_id: workout.id,
list_position: index
});
}, [workout, onSelect, index]);
const handleImageLoad = useCallback(() => {
setImageLoaded(true);
}, []);
const handleImageError = useCallback(() => {
setImageError(true);
}, []);
return (
<TouchableOpacity style={styles.workoutCard} onPress={handlePress}>
<View style={styles.imageContainer}>
{!imageError ? (
<Image
source={{ uri: workout.imageUrl }}
style={styles.workoutImage}
onLoad={handleImageLoad}
onError={handleImageError}
// Performance optimizations
resizeMode="cover"
defaultSource={require('./assets/placeholder.jpg')}
/>
) : (
<View style={styles.placeholderImage}>
<Text>No Image</Text>
</View>
)}
{!imageLoaded && !imageError && (
<View style={styles.loadingOverlay}>
<Text style={styles.loadingText}>Loading...</Text>
</View>
)}
</View>
<View style={styles.workoutInfo}>
<Text style={styles.workoutName} numberOfLines={1}>
{workout.name}
</Text>
<Text style={styles.workoutCategory} numberOfLines={1}>
{workout.category}
</Text>
<View style={styles.workoutMeta}>
<Text style={styles.duration}>{workout.duration}min</Text>
<Text style={styles.difficulty}>{workout.difficulty}</Text>
</View>
</View>
</TouchableOpacity>
);
});
// Advanced caching system for app data
class PerformanceCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.accessOrder = [];
this.maxSize = maxSize;
}
get(key) {
if (this.cache.has(key)) {
// Update access order for LRU
this.moveToFront(key);
return this.cache.get(key);
}
return null;
}
set(key, value, ttl = 300000) { // 5 minutes default TTL
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
// Remove least recently used item
const lruKey = this.accessOrder.pop();
this.cache.delete(lruKey);
}
const cacheItem = {
value,
expiry: Date.now() + ttl,
size: this.estimateSize(value)
};
this.cache.set(key, cacheItem);
this.moveToFront(key);
}
moveToFront(key) {
const index = this.accessOrder.indexOf(key);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
this.accessOrder.unshift(key);
}
// Estimate memory size of cached objects
estimateSize(obj) {
return JSON.stringify(obj).length * 2; // Rough estimate in bytes
}
// Clean expired items
cleanup() {
const now = Date.now();
for (const [key, item] of this.cache.entries()) {
if (now > item.expiry) {
this.cache.delete(key);
const index = this.accessOrder.indexOf(key);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
}
}
}
getStats() {
const totalSize = Array.from(this.cache.values())
.reduce((sum, item) => sum + (item.size || 0), 0);
return {
itemCount: this.cache.size,
totalSize,
hitRatio: this.hitCount / (this.hitCount + this.missCount) || 0
};
}
}
// Performance-optimized data service
class PerformantDataService {
constructor() {
this.cache = new PerformanceCache(200);
this.requestQueue = new Map();
this.batchTimer = null;
this.hitCount = 0;
this.missCount = 0;
}
async fetchWorkouts(category = null, forceRefresh = false) {
const cacheKey = `workouts_${category || 'all'}`;
// Check cache first
if (!forceRefresh) {
const cached = this.cache.get(cacheKey);
if (cached) {
this.hitCount++;
return cached.value;
}
}
this.missCount++;
// Batch API requests to prevent multiple calls for the same data
if (this.requestQueue.has(cacheKey)) {
return this.requestQueue.get(cacheKey);
}
const requestPromise = this.makeWorkoutRequest(category);
this.requestQueue.set(cacheKey, requestPromise);
try {
const workouts = await requestPromise;
// Cache the results
this.cache.set(cacheKey, workouts, 600000); // 10 minute cache
return workouts;
} finally {
this.requestQueue.delete(cacheKey);
}
}
async makeWorkoutRequest(category) {
performanceMonitor.startTimer('api_request_workouts');
try {
// Simulate API call
const response = await fetch(`/api/workouts${category ? `?category=${category}` : ''}`);
const data = await response.json();
performanceMonitor.endTimer('api_request_workouts', {
category,
response_size: JSON.stringify(data).length,
status: response.status
});
return data;
} catch (error) {
performanceMonitor.endTimer('api_request_workouts', {
category,
error: error.message
});
throw error;
}
}
// Prefetch data based on user behavior
async prefetchLikelyContent(userPreferences) {
const prefetchPromises = [];
// Prefetch favorite categories
userPreferences.favoriteCategories?.forEach(category => {
prefetchPromises.push(this.fetchWorkouts(category));
});
// Prefetch trending content
prefetchPromises.push(this.fetchTrendingWorkouts());
try {
await Promise.all(prefetchPromises);
console.log('Content prefetching completed');
} catch (error) {
console.warn('Some prefetch requests failed:', error);
}
}
async fetchTrendingWorkouts() {
return this.fetchWorkouts('trending');
}
}
// Performance-optimized image component
const OptimizedImage = memo(({ source, style, ...props }) => {
const [isLoaded, setIsLoaded] = useState(false);
const [hasError, setHasError] = useState(false);
return (
<View style={style}>
{!hasError && (
<Image
source={source}
style={style}
onLoad={() => setIsLoaded(true)}
onError={() => setHasError(true)}
{...props}
/>
)}
{!isLoaded && !hasError && (
<View style={[style, styles.loadingPlaceholder]}>
<Text>Loading...</Text>
</View>
)}
{hasError && (
<View style={[style, styles.errorPlaceholder]}>
<Text>Failed to load</Text>
</View>
)}
</View>
);
});
Your Mission:
Implement advanced memory management techniques:
// Advanced memory management system
class MemoryManager {
constructor() {
this.memoryWarningListeners = [];
this.backgroundTasks = new Set();
this.imageCache = new Map();
this.componentRefs = new WeakMap();
this.isLowMemoryMode = false;
}
initialize() {
// Listen for memory warnings
this.setupMemoryWarningListener();
// Periodic memory cleanup
this.startMemoryCleanup();
// Monitor large object allocations
this.setupLargeObjectMonitoring();
}
setupMemoryWarningListener() {
// React Native memory warning listener
const memoryWarningHandler = () => {
console.warn('Memory warning received - starting cleanup');
this.handleMemoryWarning();
};
// Add listener (platform-specific implementation needed)
if (Platform.OS === 'ios') {
// iOS memory warning handling
} else {
// Android memory warning handling
}
}
handleMemoryWarning() {
this.isLowMemoryMode = true;
// Clear non-essential caches
this.clearImageCache(0.7); // Clear 70% of image cache
this.clearDataCache(0.5); // Clear 50% of data cache
// Cancel non-essential background tasks
this.cancelBackgroundTasks();
// Notify components to release resources
this.notifyMemoryWarningListeners();
// Force garbage collection if available
if (global.gc) {
global.gc();
}
// Exit low memory mode after cleanup
setTimeout(() => {
this.isLowMemoryMode = false;
}, 10000);
}
clearImageCache(percentage = 0.5) {
const itemsToRemove = Math.floor(this.imageCache.size * percentage);
let removedCount = 0;
for (const [key, value] of this.imageCache.entries()) {
if (removedCount >= itemsToRemove) break;
// Remove least recently used items first
if (value.lastAccessed < Date.now() - 300000) { // 5 minutes
this.imageCache.delete(key);
removedCount++;
}
}
console.log(`Cleared ${removedCount} items from image cache`);
}
// Smart component unmounting
registerComponent(component, cleanupFn) {
this.componentRefs.set(component, {
cleanup: cleanupFn,
registeredAt: Date.now()
});
}
unregisterComponent(component) {
const componentData = this.componentRefs.get(component);
if (componentData) {
componentData.cleanup();
this.componentRefs.delete(component);
}
}
// Memory usage tracking
trackMemoryUsage(operationName, beforeSize, afterSize) {
const memoryDelta = afterSize - beforeSize;
performanceMonitor.recordMetric('memory_allocation', {
operation: operationName,
delta: memoryDelta,
timestamp: new Date().toISOString()
});
// Alert on large allocations
if (memoryDelta > 50 * 1024 * 1024) { // 50MB
console.warn(`Large memory allocation detected: ${operationName} - ${memoryDelta / 1024 / 1024}MB`);
}
}
// Lazy loading utility
createLazyComponent(importFn, fallback = null) {
return React.lazy(() => {
return importFn().then(module => {
// Track component loading
performanceMonitor.recordMetric('lazy_component_loaded', {
component: module.default.name || 'Unknown',
timestamp: new Date().toISOString()
});
return module;
});
});
}
}
// Memory-aware component hook
const useMemoryAwareComponent = (componentName, heavyResources = []) => {
const memoryManager = useRef(new MemoryManager()).current;
const resourcesRef = useRef(new Set());
useEffect(() => {
const cleanup = () => {
// Clean up heavy resources
heavyResources.forEach(resource => {
if (resource && typeof resource.cleanup === 'function') {
resource.cleanup();
}
});
// Clear component-specific caches
resourcesRef.current.clear();
};
memoryManager.registerComponent(componentName, cleanup);
return () => {
memoryManager.unregisterComponent(componentName);
};
}, [componentName, heavyResources]);
const allocateResource = useCallback((resource) => {
if (memoryManager.isLowMemoryMode) {
console.warn(`Skipping resource allocation in low memory mode: ${componentName}`);
return null;
}
resourcesRef.current.add(resource);
return resource;
}, [componentName]);
return {
allocateResource,
isLowMemoryMode: memoryManager.isLowMemoryMode,
clearResources: () => resourcesRef.current.clear()
};
};
// Performance-optimized workout component
const PerformantWorkoutComponent = memo(({ workout }) => {
const { allocateResource, isLowMemoryMode } = useMemoryAwareComponent('WorkoutComponent');
const [exerciseData, setExerciseData] = useState(null);
const videoRef = useRef(null);
// Lazy load exercise data
useEffect(() => {
const loadExerciseData = async () => {
if (isLowMemoryMode) return;
const data = allocateResource(await fetchExerciseData(workout.id));
setExerciseData(data);
};
loadExerciseData();
}, [workout.id, isLowMemoryMode, allocateResource]);
// Optimize video loading based on memory state
const shouldLoadVideo = !isLowMemoryMode && workout.hasVideo;
return (
<View style={styles.workoutContainer}>
<Text style={styles.workoutTitle}>{workout.name}</Text>
{shouldLoadVideo ? (
<Video
ref={videoRef}
source={{ uri: workout.videoUrl }}
style={styles.workoutVideo}
resizeMode="contain"
// Memory optimizations
maxBitRate={isLowMemoryMode ? 500000 : 2000000}
bufferConfig={{
minBufferMs: isLowMemoryMode ? 5000 : 15000,
maxBufferMs: isLowMemoryMode ? 10000 : 50000
}}
/>
) : (
<Image
source={{ uri: workout.thumbnailUrl }}
style={styles.workoutThumbnail}
/>
)}
{exerciseData && (
<ExerciseList
exercises={exerciseData}
renderMode={isLowMemoryMode ? 'minimal' : 'detailed'}
/>
)}
</View>
);
});
// Network optimization utilities
class NetworkOptimizer {
constructor() {
this.requestQueue = [];
this.batchSize = 5;
this.batchTimeout = 100; // ms
this.batchTimer = null;
}
// Batch multiple API requests
batchRequest(url, options = {}) {
return new Promise((resolve, reject) => {
this.requestQueue.push({ url, options, resolve, reject });
if (this.batchTimer) {
clearTimeout(this.batchTimer);
}
this.batchTimer = setTimeout(() => {
this.processBatch();
}, this.batchTimeout);
if (this.requestQueue.length >= this.batchSize) {
this.processBatch();
}
});
}
async processBatch() {
if (this.batchTimer) {
clearTimeout(this.batchTimer);
this.batchTimer = null;
}
const batch = this.requestQueue.splice(0, this.batchSize);
if (batch.length === 0) return;
const startTime = performance.now();
const promises = batch.map(async ({ url, options, resolve, reject }) => {
try {
const response = await fetch(url, options);
const data = await response.json();
resolve(data);
} catch (error) {
reject(error);
}
});
await Promise.all(promises);
const duration = performance.now() - startTime;
performanceMonitor.recordMetric('batch_request', {
batch_size: batch.length,
duration,
average_per_request: duration / batch.length
});
}
// Image optimization
optimizeImageUrl(url, width, height, quality = 80) {
if (!url) return url;
// Add image optimization parameters
const separator = url.includes('?') ? '&' : '?';
return `${url}${separator}w=${width}&h=${height}&q=${quality}&f=auto`;
}
// Preload critical resources
async preloadResources(urls) {
const preloadPromises = urls.map(url => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = resolve;
img.onerror = reject;
img.src = url;
});
});
try {
await Promise.all(preloadPromises);
console.log('Resource preloading completed');
} catch (error) {
console.warn('Some resources failed to preload:', error);
}
}
}
Create comprehensive performance monitoring dashboard:
// Performance analytics dashboard
const PerformanceDashboard = () => {
const [performanceData, setPerformanceData] = useState({});
const [alerts, setAlerts] = useState([]);
const [isRecording, setIsRecording] = useState(false);
useEffect(() => {
loadPerformanceData();
const interval = setInterval(() => {
if (isRecording) {
refreshPerformanceData();
}
}, 5000);
return () => clearInterval(interval);
}, [isRecording]);
const loadPerformanceData = async () => {
const data = {
// JavaScript thread performance
jsThreadMetrics: {
averageBlockingTime: calculateAverageBlockingTime(),
maxBlockingTime: getMaxBlockingTime(),
blockingEvents: getBlockingEvents()
},
// Memory usage
memoryMetrics: {
currentUsage: getCurrentMemoryUsage(),
peakUsage: getPeakMemoryUsage(),
averageUsage: getAverageMemoryUsage(),
garbageCollections: getGCEvents()
},
// Render performance
renderMetrics: {
averageRenderTime: getAverageRenderTime(),
slowRenders: getSlowRenders(),
componentMetrics: getComponentPerformanceStats()
},
// Network performance
networkMetrics: {
averageRequestTime: getAverageRequestTime(),
failedRequests: getFailedRequests(),
cacheHitRate: getCacheHitRate()
},
// User experience metrics
uxMetrics: {
appStartTime: getAppStartTime(),
screenTransitionTimes: getScreenTransitionTimes(),
interactionResponsiveness: getInteractionResponsiveness()
}
};
setPerformanceData(data);
setAlerts(performanceMonitor.alerts);
};
const refreshPerformanceData = async () => {
await loadPerformanceData();
};
const MetricCard = ({ title, value, unit, trend, status = 'good' }) => (
<View style={[styles.metricCard, styles[`status${status.charAt(0).toUpperCase() + status.slice(1)}`]]}>
<Text style={styles.metricTitle}>{title}</Text>
<View style={styles.metricValueContainer}>
<Text style={styles.metricValue}>{value}</Text>
<Text style={styles.metricUnit}>{unit}</Text>
</View>
{trend && (
<Text style={[styles.metricTrend, { color: trend > 0 ? '#e74c3c' : '#2ecc71' }]}>
{trend > 0 ? '↑' : '↓'} {Math.abs(trend).toFixed(1)}%
</Text>
)}
</View>
);
const AlertItem = ({ alert }) => (
<View style={[styles.alertItem, styles[`severity${alert.data.severity?.charAt(0).toUpperCase() + alert.data.severity?.slice(1)}`]]}>
<View style={styles.alertHeader}>
<Text style={styles.alertType}>{alert.type.replace(/_/g, ' ').toUpperCase()}</Text>
<Text style={styles.alertTime}>
{new Date(alert.timestamp).toLocaleTimeString()}
</Text>
</View>
<Text style={styles.alertDescription}>
{getAlertDescription(alert)}
</Text>
</View>
);
const getAlertDescription = (alert) => {
switch (alert.type) {
case 'js_thread_blocked':
return `JavaScript thread blocked for ${alert.data.delay.toFixed(1)}ms`;
case 'high_memory_usage':
return `Memory usage at ${alert.data.usagePercentage}% (${alert.data.usage.used}MB)`;
case 'slow_render':
return `Component render took ${alert.data.duration.toFixed(1)}ms`;
default:
return JSON.stringify(alert.data);
}
};
const exportPerformanceReport = async () => {
const report = {
timestamp: new Date().toISOString(),
deviceInfo: {
platform: Platform.OS,
version: Platform.Version,
model: Device.modelName
},
metrics: performanceData,
alerts: alerts,
recommendations: generatePerformanceRecommendations()
};
// Export logic (save to file, share, etc.)
console.log('Performance Report:', report);
};
const generatePerformanceRecommendations = () => {
const recommendations = [];
// JavaScript thread recommendations
if (performanceData.jsThreadMetrics?.averageBlockingTime > 16) {
recommendations.push({
category: 'JavaScript Performance',
issue: 'High JavaScript thread blocking time',
solution: 'Consider using InteractionManager.runAfterInteractions() for non-critical tasks',
priority: 'high'
});
}
// Memory recommendations
if (performanceData.memoryMetrics?.currentUsage > 200) {
recommendations.push({
category: 'Memory Usage',
issue: 'High memory consumption',
solution: 'Implement lazy loading and cleanup unused resources',
priority: 'medium'
});
}
// Render performance recommendations
if (performanceData.renderMetrics?.averageRenderTime > 16) {
recommendations.push({
category: 'Render Performance',
issue: 'Slow component rendering',
solution: 'Add React.memo(), useMemo(), and useCallback() optimizations',
priority: 'high'
});
}
return recommendations;
};
return (
<ScrollView style={styles.dashboard}>
<View style={styles.dashboardHeader}>
<Text style={styles.dashboardTitle}>Performance Dashboard</Text>
<View style={styles.controls}>
<TouchableOpacity
style={[styles.recordButton, isRecording && styles.recordingActive]}
onPress={() => setIsRecording(!isRecording)}
>
<Text style={styles.recordButtonText}>
{isRecording ? '⏹ Stop' : '⏺ Record'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.exportButton}
onPress={exportPerformanceReport}
>
<Text style={styles.exportButtonText}>📊 Export Report</Text>
</TouchableOpacity>
</View>
</View>
{/* Key Metrics */}
<View style={styles.metricsSection}>
<Text style={styles.sectionTitle}>Key Performance Metrics</Text>
<View style={styles.metricsGrid}>
<MetricCard
title="JS Thread"
value={performanceData.jsThreadMetrics?.averageBlockingTime?.toFixed(1) || '0'}
unit="ms"
status={performanceData.jsThreadMetrics?.averageBlockingTime > 16 ? 'warning' : 'good'}
/>
<MetricCard
title="Memory Usage"
value={performanceData.memoryMetrics?.currentUsage?.toFixed(0) || '0'}
unit="MB"
status={performanceData.memoryMetrics?.currentUsage > 200 ? 'critical' : 'good'}
/>
<MetricCard
title="Render Time"
value={performanceData.renderMetrics?.averageRenderTime?.toFixed(1) || '0'}
unit="ms"
status={performanceData.renderMetrics?.averageRenderTime > 16 ? 'warning' : 'good'}
/>
<MetricCard
title="Network"
value={performanceData.networkMetrics?.averageRequestTime?.toFixed(0) || '0'}
unit="ms"
status={performanceData.networkMetrics?.averageRequestTime > 1000 ? 'warning' : 'good'}
/>
</View>
</View>
{/* Performance Alerts */}
<View style={styles.alertsSection}>
<Text style={styles.sectionTitle}>Performance Alerts</Text>
{alerts.length > 0 ? (
alerts.slice(0, 10).map((alert, index) => (
<AlertItem key={alert.id || index} alert={alert} />
))
) : (
<Text style={styles.noAlertsText}>No performance issues detected</Text>
)}
</View>
{/* Performance Recommendations */}
<View style={styles.recommendationsSection}>
<Text style={styles.sectionTitle}>Optimization Recommendations</Text>
{generatePerformanceRecommendations().map((rec, index) => (
<View key={index} style={styles.recommendationCard}>
<Text style={styles.recommendationCategory}>{rec.category}</Text>
<Text style={styles.recommendationIssue}>{rec.issue}</Text>
<Text style={styles.recommendationSolution}>{rec.solution}</Text>
<View style={[styles.priorityBadge, styles[`priority${rec.priority.charAt(0).toUpperCase() + rec.priority.slice(1)}`]]}>
<Text style={styles.priorityText}>{rec.priority.toUpperCase()}</Text>
</View>
</View>
))}
</View>
</ScrollView>
);
};
Completed Successfully If:
Time Investment: 60 minutes total Difficulty Level: Advanced Prerequisites: Strong React Native knowledge, understanding of JavaScript performance Tools Needed: Performance profiling tools, device testing setup, analytics integration