/** * 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 {logErrorEvent} 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 { 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 is an error trown during render, not a Store error logErrorEvent(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 ( ); } else if (isUnsupportedBridgeOperationError) { return ( ); } else if (isUserError) { return ( 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 ( 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 ( }> ); } } return children; } _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.setState({ ...ErrorBoundary.getDerivedStateFromError(error), canDismiss: true, }); } }; }