UNPKG

3.33 kBJavaScriptView Raw
1import React from 'react';
2import { NativeModules } from 'react-native';
3import { getAppLoadingLifecycleEmitter } from './AppLoading';
4const { ExponentAppLoadingManager } = NativeModules;
5async function finishedAsync() {
6 if (ExponentAppLoadingManager && ExponentAppLoadingManager.finishedAsync) {
7 return await ExponentAppLoadingManager.finishedAsync();
8 }
9}
10// Store this outside of the component so it is available inside getDerivedStateFromError
11let _appLoadingIsMounted;
12/**
13 * This component is never rendered in production!
14 *
15 * In production the app will just hard crash on errors, unless the developer
16 * decides to handle them by overriding the global error handler and swallowing
17 * the error, in which case they are responsible for determining how to recover
18 * from this state.
19 *
20 * - The sole purpose of this component is to hide the splash screen if an
21 * error occurs that prevents it from being hidden. Please note that this
22 * currently only works with <AppLoading /> and not SplashScreen.preventAutoHide()!
23 * - We only want to update the error state when the splash screen is visible, after
24 * the splash screen is gone we don't want to do anything in this component.
25 * - On Android it is necessary for us to render some content in order to hide
26 * the splash screen, just calling `ExponentAppLoadingManager.finishedAsync()`
27 * is not sufficient.
28 *
29 */
30export default class RootErrorBoundary extends React.Component {
31 constructor(props) {
32 super(props);
33 this._subscribeToGlobalErrors = () => {
34 _appLoadingIsMounted = true;
35 const originalErrorHandler = ErrorUtils.getGlobalHandler();
36 ErrorUtils.setGlobalHandler((error, isFatal) => {
37 if (_appLoadingIsMounted) {
38 finishedAsync();
39 if (isFatal) {
40 this.setState({ error });
41 }
42 }
43 originalErrorHandler(error, isFatal);
44 });
45 };
46 this._unsubscribeFromGlobalErrors = () => {
47 // We don't remove the global error handler that we set here because it is conceivable that the
48 // user may add error handlers *after* we subscribe, and we don't want to override those, so
49 // instead we just gate the call
50 _appLoadingIsMounted = false;
51 };
52 _appLoadingIsMounted = false;
53 getAppLoadingLifecycleEmitter().once('componentDidMount', this._subscribeToGlobalErrors);
54 getAppLoadingLifecycleEmitter().once('componentWillUnmount', this._unsubscribeFromGlobalErrors);
55 this.state = {
56 error: null,
57 };
58 }
59 /**
60 * Test this by adding `throw new Error('example')` to your root component
61 * when the AppLoading component is rendered.
62 */
63 static getDerivedStateFromError(_error) {
64 if (_appLoadingIsMounted) {
65 return { error: true };
66 }
67 return null;
68 }
69 componentDidCatch(error, _errorInfo) {
70 if (_appLoadingIsMounted) {
71 finishedAsync();
72 }
73 throw error;
74 }
75 render() {
76 if (this.state.error) {
77 return null;
78 }
79 else {
80 return this.props.children;
81 }
82 }
83}
84//# sourceMappingURL=RootErrorBoundary.js.map
\No newline at end of file