@sentry/react
Version:
Official Sentry SDK for React.js
158 lines (132 loc) • 4.82 kB
JavaScript
import { getClient, showReportDialog, withScope } from '@sentry/browser';
import { logger } from '@sentry/core';
import hoistNonReactStatics from 'hoist-non-react-statics';
import * as React from 'react';
import { DEBUG_BUILD } from './debug-build.js';
import { captureReactException } from './error.js';
const UNKNOWN_COMPONENT = 'unknown';
const INITIAL_STATE = {
componentStack: null,
error: null,
eventId: null,
};
/**
* A ErrorBoundary component that logs errors to Sentry.
* NOTE: If you are a Sentry user, and you are seeing this stack frame, it means the
* Sentry React SDK ErrorBoundary caught an error invoking your application code. This
* is expected behavior and NOT indicative of a bug with the Sentry React SDK.
*/
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);ErrorBoundary.prototype.__init.call(this);
this.state = INITIAL_STATE;
this._openFallbackReportDialog = true;
const client = getClient();
if (client && props.showDialog) {
this._openFallbackReportDialog = false;
this._cleanupHook = client.on('afterSendEvent', event => {
if (!event.type && this._lastEventId && event.event_id === this._lastEventId) {
showReportDialog({ ...props.dialogOptions, eventId: this._lastEventId });
}
});
}
}
componentDidCatch(error, errorInfo) {
const { componentStack } = errorInfo;
// TODO(v9): Remove this check and type `componentStack` to be React.ErrorInfo['componentStack'].
const passedInComponentStack = componentStack == null ? undefined : componentStack;
const { beforeCapture, onError, showDialog, dialogOptions } = this.props;
withScope(scope => {
if (beforeCapture) {
beforeCapture(scope, error, passedInComponentStack);
}
const handled = this.props.handled != null ? this.props.handled : !!this.props.fallback;
const eventId = captureReactException(error, errorInfo, { mechanism: { handled } });
if (onError) {
onError(error, passedInComponentStack, eventId);
}
if (showDialog) {
this._lastEventId = eventId;
if (this._openFallbackReportDialog) {
showReportDialog({ ...dialogOptions, eventId });
}
}
// componentDidCatch is used over getDerivedStateFromError
// so that componentStack is accessible through state.
this.setState({ error, componentStack, eventId });
});
}
componentDidMount() {
const { onMount } = this.props;
if (onMount) {
onMount();
}
}
componentWillUnmount() {
const { error, componentStack, eventId } = this.state;
const { onUnmount } = this.props;
if (onUnmount) {
onUnmount(error, componentStack, eventId);
}
if (this._cleanupHook) {
this._cleanupHook();
this._cleanupHook = undefined;
}
}
__init() {this.resetErrorBoundary = () => {
const { onReset } = this.props;
const { error, componentStack, eventId } = this.state;
if (onReset) {
onReset(error, componentStack, eventId);
}
this.setState(INITIAL_STATE);
};}
render() {
const { fallback, children } = this.props;
const state = this.state;
if (state.error) {
let element = undefined;
if (typeof fallback === 'function') {
element = React.createElement(fallback, {
error: state.error,
componentStack: state.componentStack ,
resetError: this.resetErrorBoundary,
eventId: state.eventId ,
});
} else {
element = fallback;
}
if (React.isValidElement(element)) {
return element;
}
if (fallback) {
DEBUG_BUILD && logger.warn('fallback did not produce a valid ReactElement');
}
// Fail gracefully if no fallback provided or is not valid
return null;
}
if (typeof children === 'function') {
return (children )();
}
return children;
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function withErrorBoundary(
WrappedComponent,
errorBoundaryOptions,
) {
const componentDisplayName = WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
const Wrapped = (props) => (
React.createElement(ErrorBoundary, { ...errorBoundaryOptions,}
, React.createElement(WrappedComponent, { ...props,} )
)
);
Wrapped.displayName = `errorBoundary(${componentDisplayName})`;
// Copy over static methods from Wrapped component to Profiler HOC
// See: https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over
hoistNonReactStatics(Wrapped, WrappedComponent);
return Wrapped;
}
export { ErrorBoundary, UNKNOWN_COMPONENT, withErrorBoundary };
//# sourceMappingURL=errorboundary.js.map