Skip to content

Commit e819b22

Browse files
committed
test: added error boundary to suspense example
1 parent e81d0aa commit e819b22

1 file changed

Lines changed: 106 additions & 13 deletions

File tree

example/src/pages/OutOfBandAssetsWithSuspense.tsx

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
StyleSheet,
66
Text,
77
View,
8+
Button,
89
} from 'react-native';
910
import {
1011
Fit,
@@ -16,12 +17,55 @@ import {
1617
import { Picker } from '@react-native-picker/picker';
1718
import { type Metadata } from '../helpers/metadata';
1819

20+
class ErrorBoundary extends React.Component<
21+
{
22+
children: React.ReactNode;
23+
fallback?: (error: Error, reset: () => void) => React.ReactNode;
24+
},
25+
{ hasError: boolean; error: Error | null }
26+
> {
27+
constructor(props: any) {
28+
super(props);
29+
this.state = { hasError: false, error: null };
30+
}
31+
32+
static getDerivedStateFromError(error: Error) {
33+
return { hasError: true, error };
34+
}
35+
36+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
37+
console.error('ErrorBoundary caught:', error, errorInfo);
38+
}
39+
40+
reset = () => {
41+
this.setState({ hasError: false, error: null });
42+
};
43+
44+
render() {
45+
if (this.state.hasError && this.state.error) {
46+
if (this.props.fallback) {
47+
return this.props.fallback(this.state.error, this.reset);
48+
}
49+
return (
50+
<View style={styles.errorContainer}>
51+
<Text style={styles.errorText}>Something went wrong:</Text>
52+
<Text style={styles.errorMessage}>{this.state.error.message}</Text>
53+
<Button title="Try Again" onPress={this.reset} />
54+
</View>
55+
);
56+
}
57+
58+
return this.props.children;
59+
}
60+
}
61+
1962
const delay = 1000;
2063

2164
const ImageURL1 = `https://picsum.photos/id/372/500/500` as const;
2265
const ImageURL2 = `https://picsum.photos/id/373/500/500` as const;
2366
const ImageURLSlow =
2467
`https://app.requestly.io/delay/${delay}/https://picsum.photos/id/374/500/500` as const;
68+
const ImageInvalidURL = 'not-a-valid-url' as const;
2569

2670
type ImageURLS = typeof ImageURL1 | typeof ImageURL2 | typeof ImageURLSlow;
2771

@@ -73,21 +117,48 @@ function RiveContent({ imageUrl }: { imageUrl: ImageURLS }) {
73117
);
74118
}
75119

120+
function ErrorFallback({ error, reset }: { error: Error; reset: () => void }) {
121+
return (
122+
<View style={styles.errorContainer}>
123+
<Text style={styles.errorText}>Error loading image:</Text>
124+
<Text style={styles.errorMessage}>{error.message}</Text>
125+
<Button title="Try Again" onPress={reset} />
126+
</View>
127+
);
128+
}
129+
76130
export default function OutOfBandAssetsWithSuspenseExample() {
77131
const [uri, setUri] = React.useState<ImageURLS>(ImageURL1);
132+
const [errorBoundaryKey, setErrorBoundaryKey] = React.useState(0);
133+
134+
const handleReset = () => {
135+
setErrorBoundaryKey((k) => k + 1);
136+
};
137+
138+
const renderErrorFallback = (error: Error, reset: () => void) => (
139+
<ErrorFallback
140+
error={error}
141+
reset={() => {
142+
reset();
143+
handleReset();
144+
}}
145+
/>
146+
);
78147

79148
return (
80149
<View style={styles.safeAreaViewContainer}>
81-
<React.Suspense
82-
fallback={
83-
<View style={styles.loadingContainer}>
84-
<ActivityIndicator size="large" />
85-
<Text style={styles.loadingText}>Loading image...</Text>
86-
</View>
87-
}
88-
>
89-
<RiveContent imageUrl={uri} />
90-
</React.Suspense>
150+
<ErrorBoundary key={errorBoundaryKey} fallback={renderErrorFallback}>
151+
<React.Suspense
152+
fallback={
153+
<View style={styles.loadingContainer}>
154+
<ActivityIndicator size="large" />
155+
<Text style={styles.loadingText}>Loading image...</Text>
156+
</View>
157+
}
158+
>
159+
<RiveContent imageUrl={uri} />
160+
</React.Suspense>
161+
</ErrorBoundary>
91162

92163
<ScrollView contentContainerStyle={styles.container}>
93164
<View style={styles.pickersWrapper}>
@@ -102,9 +173,11 @@ export default function OutOfBandAssetsWithSuspenseExample() {
102173
mode={'dropdown'}
103174
style={styles.picker}
104175
>
105-
{[ImageURL1, ImageURL2, ImageURLSlow].map((key) => (
106-
<Picker.Item key={key} value={key} label={key} />
107-
))}
176+
{[ImageURL1, ImageURL2, ImageURLSlow, ImageInvalidURL].map(
177+
(url) => (
178+
<Picker.Item key={url} value={url} label={url} />
179+
)
180+
)}
108181
</Picker>
109182
</View>
110183
</View>
@@ -159,6 +232,26 @@ const styles = StyleSheet.create({
159232
marginBottom: 8,
160233
paddingHorizontal: 16,
161234
},
235+
errorContainer: {
236+
width: '100%',
237+
height: 400,
238+
justifyContent: 'center',
239+
alignItems: 'center',
240+
padding: 16,
241+
backgroundColor: '#ffebee',
242+
},
243+
errorText: {
244+
fontSize: 18,
245+
fontWeight: 'bold',
246+
color: '#c62828',
247+
marginBottom: 8,
248+
},
249+
errorMessage: {
250+
fontSize: 14,
251+
color: '#d32f2f',
252+
marginBottom: 16,
253+
textAlign: 'center',
254+
},
162255
});
163256

164257
OutOfBandAssetsWithSuspenseExample.metadata = {

0 commit comments

Comments
 (0)