/ components / ErrorBoundary.tsx
ErrorBoundary.tsx
1 import React from 'react'; 2 import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; 3 import { Ionicons } from '@expo/vector-icons'; 4 import { Colors } from '@/constants/Colors'; 5 import { useTheme } from '@/constants/ThemeContext'; 6 7 interface ErrorBoundaryState { 8 hasError: boolean; 9 error?: Error | undefined; 10 errorInfo?: React.ErrorInfo | undefined; 11 } 12 13 interface ErrorBoundaryProps { 14 children: React.ReactNode; 15 fallback?: React.ComponentType<{ error?: Error | undefined; resetError: () => void }>; 16 } 17 18 class ErrorBoundary extends React.Component< 19 ErrorBoundaryProps, 20 ErrorBoundaryState 21 > { 22 constructor(props: ErrorBoundaryProps) { 23 super(props); 24 this.state = { hasError: false }; 25 } 26 27 static getDerivedStateFromError(error: Error): ErrorBoundaryState { 28 return { hasError: true, error }; 29 } 30 31 override componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { 32 console.error('ErrorBoundary caught an error:', error, errorInfo); 33 this.setState({ error, errorInfo }); 34 } 35 36 resetError = () => { 37 this.setState({ hasError: false, error: undefined as Error | undefined, errorInfo: undefined as React.ErrorInfo | undefined }); 38 }; 39 40 override render() { 41 if (this.state.hasError) { 42 if (this.props.fallback) { 43 const FallbackComponent = this.props.fallback; 44 return ( 45 <FallbackComponent 46 error={this.state.error as Error | undefined} 47 resetError={this.resetError} 48 /> 49 ); 50 } 51 52 return ( 53 <DefaultErrorFallback 54 error={this.state.error as Error | undefined} 55 resetError={this.resetError} 56 /> 57 ); 58 } 59 60 return this.props.children; 61 } 62 } 63 64 const DefaultErrorFallback: React.FC<{ 65 error?: Error | undefined; 66 resetError: () => void; 67 }> = ({ error, resetError }) => { 68 const { actualTheme } = useTheme(); 69 const colors = Colors[actualTheme]; 70 const styles = getStyles(colors); 71 72 return ( 73 <View style={styles.container}> 74 <Ionicons 75 name="alert-circle-outline" 76 size={64} 77 color={colors.notification} 78 /> 79 <Text style={styles.title}>Something went wrong</Text> 80 <Text style={styles.message}> 81 {error?.message || 'An unexpected error occurred'} 82 </Text> 83 <TouchableOpacity style={styles.button} onPress={resetError}> 84 <Text style={styles.buttonText}>Try Again</Text> 85 </TouchableOpacity> 86 </View> 87 ); 88 }; 89 90 const getStyles = (colors: typeof Colors.light) => 91 StyleSheet.create({ 92 container: { 93 flex: 1, 94 justifyContent: 'center', 95 alignItems: 'center', 96 padding: 20, 97 backgroundColor: colors.background, 98 }, 99 title: { 100 fontSize: 24, 101 fontWeight: 'bold', 102 color: colors.text, 103 marginTop: 16, 104 marginBottom: 8, 105 }, 106 message: { 107 fontSize: 16, 108 color: colors.text, 109 textAlign: 'center', 110 marginBottom: 24, 111 opacity: 0.8, 112 }, 113 button: { 114 backgroundColor: colors.primary, 115 paddingHorizontal: 24, 116 paddingVertical: 12, 117 borderRadius: 8, 118 }, 119 buttonText: { 120 color: '#FFFFFF', 121 fontSize: 16, 122 fontWeight: 'bold', 123 }, 124 }); 125 126 export default ErrorBoundary;