Skip to content

Commit 4e80697

Browse files
committed
feat(react)!: Update ErrorBoundary componentStack type
1 parent edbb214 commit 4e80697

File tree

3 files changed

+32
-36
lines changed

3 files changed

+32
-36
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
1212

13-
Work in this release was contributed by @antonis, and @maximepvrt. Thank you for your contributions!
13+
Work in this release was contributed by @antonis, @maximepvrt, and @kunal-511. Thank you for your contributions!
1414

1515
## 8.45.0
1616

docs/migration/v8-to-v9.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ Sentry.init({
7070

7171
- When `skipOpenTelemetrySetup: true` is configured, `httpIntegration({ spans: false })` will be configured by default. This means that you no longer have to specify this yourself in this scenario. With this change, no spans are emitted once `skipOpenTelemetrySetup: true` is configured, without any further configuration being needed.
7272

73+
### `@sentry/react`
74+
75+
The `componentStack` field in the `ErrorBoundary` component is now typed as `string | undefined` instead of `string | null | undefined`. This more closely matches the actual behavior of React, which always returns a `string` whenever a component stack is available. `undefined` is only returned if no error has been caught by the error boundary.
76+
7377
### Uncategorized (TODO)
7478

7579
TODO

packages/react/src/errorboundary.tsx

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ export type ErrorBoundaryProps = {
3636
*/
3737
fallback?: React.ReactElement | FallbackRender | undefined;
3838
/** Called when the error boundary encounters an error */
39-
onError?: ((error: unknown, componentStack: string | undefined, eventId: string) => void) | undefined;
39+
onError?: ((error: unknown, componentStack: string, eventId: string) => void) | undefined;
4040
/** Called on componentDidMount() */
4141
onMount?: (() => void) | undefined;
4242
/** Called if resetError() is called from the fallback render props function */
43-
onReset?: ((error: unknown, componentStack: string | null | undefined, eventId: string | null) => void) | undefined;
43+
onReset?: ((error: unknown, componentStack: string | null, eventId: string | null) => void) | undefined;
4444
/** Called on componentWillUnmount() */
45-
onUnmount?: ((error: unknown, componentStack: string | null | undefined, eventId: string | null) => void) | undefined;
45+
onUnmount?: ((error: unknown, componentStack: string | null, eventId: string | null) => void) | undefined;
4646
/** Called before the error is captured by Sentry, allows for you to add tags or context using the scope */
47-
beforeCapture?: ((scope: Scope, error: unknown, componentStack: string | undefined) => void) | undefined;
47+
beforeCapture?: ((scope: Scope, error: unknown, componentStack: string) => void) | undefined;
4848
};
4949

5050
type ErrorBoundaryState =
@@ -59,7 +59,7 @@ type ErrorBoundaryState =
5959
eventId: string;
6060
};
6161

62-
const INITIAL_STATE = {
62+
const INITIAL_STATE: ErrorBoundaryState = {
6363
componentStack: null,
6464
error: null,
6565
eventId: null,
@@ -98,19 +98,16 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
9898

9999
public componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void {
100100
const { componentStack } = errorInfo;
101-
// TODO(v9): Remove this check and type `componentStack` to be React.ErrorInfo['componentStack'].
102-
const passedInComponentStack: string | undefined = componentStack == null ? undefined : componentStack;
103-
104101
const { beforeCapture, onError, showDialog, dialogOptions } = this.props;
105102
withScope(scope => {
106103
if (beforeCapture) {
107-
beforeCapture(scope, error, passedInComponentStack);
104+
beforeCapture(scope, error, componentStack);
108105
}
109106

110107
const eventId = captureReactException(error, errorInfo, { mechanism: { handled: !!this.props.fallback } });
111108

112109
if (onError) {
113-
onError(error, passedInComponentStack, eventId);
110+
onError(error, componentStack, eventId);
114111
}
115112
if (showDialog) {
116113
this._lastEventId = eventId;
@@ -158,35 +155,30 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
158155
const { fallback, children } = this.props;
159156
const state = this.state;
160157

161-
if (state.error) {
162-
let element: React.ReactElement | undefined = undefined;
163-
if (typeof fallback === 'function') {
164-
element = React.createElement(fallback, {
165-
error: state.error,
166-
componentStack: state.componentStack as string,
167-
resetError: this.resetErrorBoundary,
168-
eventId: state.eventId as string,
169-
});
170-
} else {
171-
element = fallback;
172-
}
173-
174-
if (React.isValidElement(element)) {
175-
return element;
176-
}
177-
178-
if (fallback) {
179-
DEBUG_BUILD && logger.warn('fallback did not produce a valid ReactElement');
180-
}
158+
if (state.error === null) {
159+
return typeof children === 'function' ? children() : children;
160+
}
181161

182-
// Fail gracefully if no fallback provided or is not valid
183-
return null;
162+
const element =
163+
typeof fallback === 'function'
164+
? React.createElement(fallback, {
165+
error: state.error,
166+
componentStack: state.componentStack,
167+
resetError: this.resetErrorBoundary,
168+
eventId: state.eventId,
169+
})
170+
: fallback;
171+
172+
if (React.isValidElement(element)) {
173+
return element;
184174
}
185175

186-
if (typeof children === 'function') {
187-
return (children as () => React.ReactNode)();
176+
if (fallback) {
177+
DEBUG_BUILD && logger.warn('fallback did not produce a valid ReactElement');
188178
}
189-
return children;
179+
180+
// Fail gracefully if no fallback provided or is not valid
181+
return null;
190182
}
191183
}
192184

0 commit comments

Comments
 (0)