Skip to content

Commit 99745d6

Browse files
authored
feat(react): Expose eventId on React ErrorBoundary (#2704)
1 parent de1b2c9 commit 99745d6

File tree

3 files changed

+25
-17
lines changed

3 files changed

+25
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66
- [tracing] fix: Add manual Location typing (#2700)
7+
- [react] feat: Expose eventId on ErrorBoundary component
78

89
## 5.18.1
910

packages/react/src/errorboundary.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type FallbackRender = (fallback: {
88
error: Error | null;
99
componentStack: string | null;
1010
resetError(): void;
11+
eventId: string | null;
1112
}) => React.ReactNode;
1213

1314
export type ErrorBoundaryProps = {
@@ -30,23 +31,25 @@ export type ErrorBoundaryProps = {
3031
fallback?: React.ReactNode | FallbackRender;
3132
// tslint:enable no-null-undefined-union
3233
/** Called with the error boundary encounters an error */
33-
onError?(error: Error, componentStack: string): void;
34+
onError?(error: Error, componentStack: string, eventId: string): void;
3435
/** Called on componentDidMount() */
3536
onMount?(): void;
3637
/** Called if resetError() is called from the fallback render props function */
37-
onReset?(error: Error | null, componentStack: string | null): void;
38+
onReset?(error: Error | null, componentStack: string | null, eventId: string | null): void;
3839
/** Called on componentWillUnmount() */
39-
onUnmount?(error: Error | null, componentStack: string | null): void;
40+
onUnmount?(error: Error | null, componentStack: string | null, eventId: string | null): void;
4041
};
4142

4243
type ErrorBoundaryState = {
4344
componentStack: string | null;
4445
error: Error | null;
46+
eventId: string | null;
4547
};
4648

4749
const INITIAL_STATE = {
4850
componentStack: null,
4951
error: null,
52+
eventId: null,
5053
};
5154

5255
/**
@@ -60,15 +63,15 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
6063
const eventId = Sentry.captureException(error, { contexts: { react: { componentStack } } });
6164
const { onError, showDialog, dialogOptions } = this.props;
6265
if (onError) {
63-
onError(error, componentStack);
66+
onError(error, componentStack, eventId);
6467
}
6568
if (showDialog) {
6669
Sentry.showReportDialog({ ...dialogOptions, eventId });
6770
}
6871

6972
// componentDidCatch is used over getDerivedStateFromError
7073
// so that componentStack is accessible through state.
71-
this.setState({ error, componentStack });
74+
this.setState({ error, componentStack, eventId });
7275
}
7376

7477
public componentDidMount(): void {
@@ -79,31 +82,32 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
7982
}
8083

8184
public componentWillUnmount(): void {
82-
const { error, componentStack } = this.state;
85+
const { error, componentStack, eventId } = this.state;
8386
const { onUnmount } = this.props;
8487
if (onUnmount) {
85-
onUnmount(error, componentStack);
88+
onUnmount(error, componentStack, eventId);
8689
}
8790
}
8891

8992
public resetErrorBoundary = () => {
9093
const { onReset } = this.props;
94+
const { error, componentStack, eventId } = this.state;
9195
if (onReset) {
92-
onReset(this.state.error, this.state.componentStack);
96+
onReset(error, componentStack, eventId);
9397
}
9498
this.setState(INITIAL_STATE);
9599
};
96100

97101
public render(): React.ReactNode {
98102
const { fallback } = this.props;
99-
const { error, componentStack } = this.state;
103+
const { error, componentStack, eventId } = this.state;
100104

101105
if (error) {
102106
if (React.isValidElement(fallback)) {
103107
return fallback;
104108
}
105109
if (typeof fallback === 'function') {
106-
return fallback({ error, componentStack, resetError: this.resetErrorBoundary }) as FallbackRender;
110+
return fallback({ error, componentStack, resetError: this.resetErrorBoundary, eventId }) as FallbackRender;
107111
}
108112

109113
// Fail gracefully if no fallback provided

packages/react/test/errorboundary.test.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ const TestApp: React.FC<ErrorBoundaryProps> = ({ children, ...props }) => {
2222
return (
2323
<ErrorBoundary
2424
{...props}
25-
onReset={(err: Error, stack: string) => {
25+
onReset={(...args) => {
2626
setError(false);
2727
if (props.onReset) {
28-
props.onReset(err, stack);
28+
props.onReset(...args);
2929
}
3030
}}
3131
>
@@ -107,7 +107,7 @@ describe('ErrorBoundary', () => {
107107
expect(mockOnUnmount).toHaveBeenCalledTimes(0);
108108
unmount();
109109
expect(mockOnUnmount).toHaveBeenCalledTimes(1);
110-
expect(mockOnUnmount).toHaveBeenCalledWith(null, null);
110+
expect(mockOnUnmount).toHaveBeenCalledWith(null, null, null);
111111
});
112112

113113
it('renders children correctly when there is no error', () => {
@@ -140,12 +140,14 @@ describe('ErrorBoundary', () => {
140140
it('renders a render props component', async () => {
141141
let errorString = '';
142142
let compStack = '';
143+
let eventIdString = '';
143144
const { container } = render(
144145
<TestApp
145-
fallback={({ error, componentStack }) => {
146-
if (error && componentStack) {
146+
fallback={({ error, componentStack, eventId }) => {
147+
if (error && componentStack && eventId) {
147148
errorString = error.toString();
148149
compStack = componentStack;
150+
eventIdString = eventId;
149151
}
150152
return <div>Fallback here</div>;
151153
}}
@@ -167,6 +169,7 @@ describe('ErrorBoundary', () => {
167169
in Bam (created by TestApp)
168170
in ErrorBoundary (created by TestApp)
169171
in TestApp`);
172+
expect(eventIdString).toBe(EVENT_ID);
170173
});
171174
});
172175

@@ -186,7 +189,7 @@ describe('ErrorBoundary', () => {
186189
fireEvent.click(btn);
187190

188191
expect(mockOnError).toHaveBeenCalledTimes(1);
189-
expect(mockOnError).toHaveBeenCalledWith(expect.any(Error), expect.any(String));
192+
expect(mockOnError).toHaveBeenCalledWith(expect.any(Error), expect.any(String), expect.any(String));
190193

191194
expect(mockCaptureException).toHaveBeenCalledTimes(1);
192195
expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error), {
@@ -249,7 +252,7 @@ describe('ErrorBoundary', () => {
249252
fireEvent.click(reset);
250253

251254
expect(mockOnReset).toHaveBeenCalledTimes(1);
252-
expect(mockOnReset).toHaveBeenCalledWith(expect.any(Error), expect.any(String));
255+
expect(mockOnReset).toHaveBeenCalledWith(expect.any(Error), expect.any(String), expect.any(String));
253256
});
254257
});
255258
});

0 commit comments

Comments
 (0)