/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import * as React from 'react';
import {Component, Suspense} from 'react';
import Store from 'react-devtools-shared/src/devtools/store';
import UnsupportedBridgeOperationView from './UnsupportedBridgeOperationView';
import ErrorView from './ErrorView';
import SearchingGitHubIssues from './SearchingGitHubIssues';
import SuspendingErrorView from './SuspendingErrorView';
import TimeoutView from './TimeoutView';
import CaughtErrorView from './CaughtErrorView';
import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError';
import TimeoutError from 'react-devtools-shared/src/errors/TimeoutError';
import UserError from 'react-devtools-shared/src/errors/UserError';
import UnknownHookError from 'react-devtools-shared/src/errors/UnknownHookError';
import {logEvent} from 'react-devtools-shared/src/Logger';
type Props = {
children: React$Node,
canDismiss?: boolean,
onBeforeDismissCallback?: () => void,
store?: Store,
};type State = {
callStack: string | null,
canDismiss: boolean,
componentStack: string | null,
errorMessage: string | null,
hasError: boolean,
isUnsupportedBridgeOperationError: boolean,
isTimeout: boolean,
isUserError: boolean,
isUnknownHookError: boolean,
};const InitialState: State = {
callStack: null,
canDismiss: false,
componentStack: null,
errorMessage: null,
hasError: false,
isUnsupportedBridgeOperationError: false,
isTimeout: false,
isUserError: false,
isUnknownHookError: false,
};export default class ErrorBoundary extends Component<Props, State> {
state: State = InitialState;static getDerivedStateFromError(error: any): {
callStack: string | null,errorMessage: string | null,hasError: boolean,isTimeout: boolean,isUnknownHookError: boolean,isUnsupportedBridgeOperationError: boolean,isUserError: boolean,} {
const errorMessage =
typeof error === 'object' &&
error !== null &&
typeof error.message === 'string'
? error.message
: null;
const isTimeout = error instanceof TimeoutError;
const isUserError = error instanceof UserError;
const isUnknownHookError = error instanceof UnknownHookError;
const isUnsupportedBridgeOperationError =
error instanceof UnsupportedBridgeOperationError;
const callStack =
typeof error === 'object' &&
error !== null &&
typeof error.stack === 'string'
? error.stack.split('\n').slice(1).join('\n')
: null;
return {
callStack,
errorMessage,
hasError: true,
isUnsupportedBridgeOperationError,
isUnknownHookError,
isTimeout,
isUserError,
};}componentDidCatch(error: any, {componentStack}: any) {
this._logError(error, componentStack);
this.setState({
componentStack,
});}componentDidMount() {
const {store} = this.props;
if (store != null) {
store.addListener('error', this._onStoreError);
}}componentWillUnmount() {
const {store} = this.props;
if (store != null) {
store.removeListener('error', this._onStoreError);
}}render(): React.Node {
const {canDismiss: canDismissProp, children} = this.props;
const {
callStack,
canDismiss: canDismissState,
componentStack,
errorMessage,
hasError,
isUnsupportedBridgeOperationError,
isTimeout,
isUserError,
isUnknownHookError,
} = this.state;
if (hasError) {
if (isTimeout) {
return (
<TimeoutView
callStack={callStack}
componentStack={componentStack}
dismissError={
canDismissProp || canDismissState ? this._dismissError : null
}errorMessage={errorMessage}
/>);
} else if (isUnsupportedBridgeOperationError) {
return (
<UnsupportedBridgeOperationViewcallStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage}
/>);
} else if (isUserError) {
return (
<CaughtErrorViewcallStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage || 'Error occured in inspected element'}
info={
<>React DevTools encountered an error while trying to inspect the
hooks. This is most likely caused by a developer error in the
currently inspected element. Please see your console for logged
error.</>}/>);
} else if (isUnknownHookError) {
return (
<CaughtErrorViewcallStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage || 'Encountered an unknown hook'}
info={
<>React DevTools encountered an unknown hook. This is probably
because the react-debug-tools package is out of date. To fix,
upgrade the React DevTools to the most recent version.</>}/>);
} else {
return (
<ErrorViewcallStack={callStack}
componentStack={componentStack}
dismissError={
canDismissProp || canDismissState ? this._dismissError : null
}errorMessage={errorMessage}>
<Suspense fallback={<SearchingGitHubIssues />}>
<SuspendingErrorView
callStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage}
/></Suspense>
</ErrorView>
);
}}return children;
}_logError: (error: any, componentStack: string | null) => void = (
error,
componentStack,
) => {
logEvent({
event_name: 'error',error_message: error.message ?? null,error_stack: error.stack ?? null,error_component_stack: componentStack ?? null,});};_dismissError: () => void = () => {
const onBeforeDismissCallback = this.props.onBeforeDismissCallback;
if (typeof onBeforeDismissCallback === 'function') {
onBeforeDismissCallback();
}this.setState(InitialState);
};_onStoreError: (error: Error) => void = error => {
if (!this.state.hasError) {
this._logError(error, null);
this.setState({
...ErrorBoundary.getDerivedStateFromError(error),
canDismiss: true,
});}};}