diff --git a/packages/react/src/errorboundary.tsx b/packages/react/src/errorboundary.tsx
index 96a88cf31e73..9a01ae110a64 100644
--- a/packages/react/src/errorboundary.tsx
+++ b/packages/react/src/errorboundary.tsx
@@ -1,5 +1,5 @@
import type { ReportDialogOptions, Scope } from '@sentry/browser';
-import { captureException, showReportDialog, withScope } from '@sentry/browser';
+import { captureException, getCurrentHub, showReportDialog, withScope } from '@sentry/browser';
import { isError, logger } from '@sentry/utils';
import hoistNonReactStatics from 'hoist-non-react-statics';
import * as React from 'react';
@@ -94,9 +94,26 @@ function setCause(error: Error & { cause?: Error }, cause: Error): void {
class ErrorBoundary extends React.Component {
public state: ErrorBoundaryState = INITIAL_STATE;
+ private readonly _openFallbackReportDialog: boolean = true;
+
+ private _lastEventId?: string;
+
+ public constructor(props: ErrorBoundaryProps) {
+ super(props);
+
+ const client = getCurrentHub().getClient();
+ if (client && client.on && props.showDialog) {
+ this._openFallbackReportDialog = false;
+ client.on('afterSendEvent', event => {
+ if (!event.type && event.event_id === this._lastEventId) {
+ showReportDialog({ ...props.dialogOptions, eventId: this._lastEventId });
+ }
+ });
+ }
+ }
+
public componentDidCatch(error: Error & { cause?: Error }, { componentStack }: React.ErrorInfo): void {
const { beforeCapture, onError, showDialog, dialogOptions } = this.props;
-
withScope(scope => {
// If on React version >= 17, create stack trace from componentStack param and links
// to to the original error using `error.cause` otherwise relies on error param for stacktrace.
@@ -123,7 +140,10 @@ class ErrorBoundary extends React.Component {
@@ -82,6 +83,7 @@ describe('ErrorBoundary', () => {
afterEach(() => {
mockCaptureException.mockClear();
mockShowReportDialog.mockClear();
+ mockClientOn.mockClear();
});
it('renders null if not given a valid `fallback` prop', () => {
@@ -405,7 +407,34 @@ describe('ErrorBoundary', () => {
expect(mockCaptureException).toHaveBeenCalledTimes(1);
});
- it('shows a Sentry Report Dialog with correct options', () => {
+ it('shows a Sentry Report Dialog with correct options if client does not have hooks', () => {
+ expect(getCurrentHub().getClient()).toBeUndefined();
+
+ const options = { title: 'custom title' };
+ render(
+ You have hit an error
} showDialog dialogOptions={options}>
+ children
+ ,
+ );
+
+ expect(mockShowReportDialog).toHaveBeenCalledTimes(0);
+
+ const btn = screen.getByTestId('errorBtn');
+ fireEvent.click(btn);
+
+ expect(mockShowReportDialog).toHaveBeenCalledTimes(1);
+ expect(mockShowReportDialog).toHaveBeenCalledWith({ ...options, eventId: EVENT_ID });
+ });
+
+ it('shows a Sentry Report Dialog with correct options if client has hooks', () => {
+ let callback: any;
+ const hub = getCurrentHub();
+ // @ts-ignore mock client
+ hub.bindClient({
+ on: (name: string, cb: any) => {
+ callback = cb;
+ },
+ });
const options = { title: 'custom title' };
render(
You have hit an error} showDialog dialogOptions={options}>
@@ -418,8 +447,13 @@ describe('ErrorBoundary', () => {
const btn = screen.getByTestId('errorBtn');
fireEvent.click(btn);
+ // Simulate hook being fired
+ callback({ event_id: EVENT_ID });
+
expect(mockShowReportDialog).toHaveBeenCalledTimes(1);
expect(mockShowReportDialog).toHaveBeenCalledWith({ ...options, eventId: EVENT_ID });
+
+ hub.bindClient(undefined);
});
it('resets to initial state when reset', async () => {