Kantongku: hardening - ErrorBoundary + catch semua load async (anti force-close)

This commit is contained in:
Bro
2026-08-16 21:20:39 +07:00
parent 639f3a4ef6
commit cb22c1394a
6 changed files with 50 additions and 29 deletions
+25 -1
View File
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { Component, useEffect, useState } from 'react';
import type { ReactNode } from 'react';
import { ActivityIndicator, StatusBar, StyleSheet, Text, View } from 'react-native';
import * as SplashScreen from 'expo-splash-screen';
import { SafeAreaProvider } from 'react-native-safe-area-context';
@@ -11,6 +12,27 @@ import { colors } from './src/theme/colors';
SplashScreen.preventAutoHideAsync().catch(() => {});
/** Tangkap error render agar tidak force-close: tampilkan layar aman. */
class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean; message: string }> {
state = { hasError: false, message: '' };
static getDerivedStateFromError(e: unknown) {
return { hasError: true, message: e instanceof Error ? e.message : String(e) };
}
render() {
if (this.state.hasError) {
return (
<View style={styles.center}>
<Text style={styles.errorTitle}>Terjadi kesalahan 😢</Text>
<Text style={styles.errorText}>{this.state.message}</Text>
</View>
);
}
return this.props.children;
}
}
export default function App() {
const [ready, setReady] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -62,6 +84,7 @@ export default function App() {
}
return (
<ErrorBoundary>
<SafeAreaProvider>
<NavigationContainer>
<RootNavigator initialRoute={initialRoute} />
@@ -72,6 +95,7 @@ export default function App() {
backgroundColor={colors.primary}
/>
</SafeAreaProvider>
</ErrorBoundary>
);
}
+1 -3
View File
@@ -116,9 +116,7 @@ export function DashboardScreen() {
useFocusEffect(
useCallback(() => {
let active = true;
load().then(() => {
if (!active) return;
});
load().catch(() => {});
return () => {
active = false;
};
+1 -3
View File
@@ -60,9 +60,7 @@ export function HistoryScreen() {
useFocusEffect(
useCallback(() => {
let active = true;
load().then(() => {
if (!active) return;
});
load().catch(() => {});
return () => {
active = false;
};
+5
View File
@@ -27,10 +27,15 @@ export function OnboardingScreen() {
const [gender, setGender] = useState<'male' | 'female' | null>(null);
const handleFinish = async () => {
try {
await setSetting('user_name', name.trim());
await setSetting('user_gender', gender ?? 'male');
await setSetting('onboarded', '1');
navigation.replace('Main');
} catch {
// tetap lanjut walau gagal simpan
navigation.replace('Main');
}
};
return (
+1 -3
View File
@@ -69,9 +69,7 @@ export function SavingsScreen() {
useFocusEffect(
useCallback(() => {
let active = true;
load().then(() => {
if (!active) return;
});
load().catch(() => {});
return () => {
active = false;
};
+3 -5
View File
@@ -102,15 +102,13 @@ export function SettingsScreen() {
useFocusEffect(
useCallback(() => {
let active = true;
loadSettings().then(() => {
if (!active) return;
});
loadSettings().catch(() => {});
getRecurring().then((r) => {
if (active) setRecurring(r);
});
}).catch(() => {});
getCategories().then((c) => {
if (active) setCategories(c);
});
}).catch(() => {});
return () => {
active = false;
};