Skip to content

Commit a82ce2f

Browse files
committed
feat(react): Setup basic error boundary props
1 parent 6747222 commit a82ce2f

File tree

4 files changed

+70
-28
lines changed

4 files changed

+70
-28
lines changed

packages/react/src/errorboundary.tsx

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,61 @@
1-
import * as React from 'react';
21
import * as Sentry from '@sentry/browser';
2+
import * as React from 'react';
33

4-
interface ErrorBoundaryProps {}
4+
export type ErrorBoundaryProps = {
5+
fallback?: React.ReactNode;
6+
fallbackRender?(error: Error | null, componentStack: string | null, resetErrorBoundary: () => void): React.ReactNode;
7+
onError?(error: Error, componentStack: string): void;
8+
onReset?(error: Error | null, componentStack: string | null): void;
9+
};
510

6-
interface ErrorBoundaryState {
7-
hasError: boolean;
8-
}
11+
type ErrorBoundaryState = {
12+
error: Error | null;
13+
componentStack: string | null;
14+
};
15+
16+
const INITIAL_STATE = {
17+
componentStack: null,
18+
error: null,
19+
};
920

1021
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
11-
constructor(props: ErrorBoundaryProps) {
12-
super(props);
13-
this.state = {
14-
hasError: false,
15-
};
16-
}
22+
public state: ErrorBoundaryState = INITIAL_STATE;
1723

18-
public static getDerivedStateFromError(_: Error): ErrorBoundaryState {
19-
return { hasError: true };
24+
public componentDidCatch(error: Error, { componentStack }: React.ErrorInfo): void {
25+
Sentry.withScope(scope => {
26+
scope.setExtra('componentStack', componentStack);
27+
Sentry.captureException(error);
28+
});
29+
const { onError } = this.props;
30+
if (onError) {
31+
onError(error, componentStack);
32+
}
33+
this.setState({ error, componentStack });
2034
}
2135

22-
public componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
23-
Sentry.captureException(Error);
24-
console.log(error);
25-
console.log(errorInfo.componentStack);
26-
}
36+
public resetErrorBoundary = () => {
37+
const { onReset } = this.props;
38+
if (onReset) {
39+
onReset(this.state.error, this.state.componentStack);
40+
}
41+
this.setState(INITIAL_STATE);
42+
};
2743

2844
public render(): React.ReactNode {
29-
if (this.state.hasError) {
30-
return null;
45+
const { fallback, fallbackRender } = this.props;
46+
const { error, componentStack } = this.state;
47+
48+
if (error) {
49+
if (typeof fallbackRender === 'function') {
50+
return fallbackRender(error, componentStack, this.resetErrorBoundary);
51+
}
52+
if (React.isValidElement(fallback)) {
53+
return fallback;
54+
}
55+
56+
throw new Error('No fallback component has been set');
3157
}
58+
3259
return this.props.children;
3360
}
3461
}

packages/react/src/profiler.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ const getInitActivity = (name: string): number | null => {
5656
return null;
5757
};
5858

59-
interface ProfilerProps {
59+
export type ProfilerProps = {
6060
name: string;
61-
}
61+
};
6262

6363
class Profiler extends React.Component<ProfilerProps> {
6464
public activity: number | null;

packages/react/test/errorboundary.test.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
1+
import { render } from '@testing-library/react';
12
import * as React from 'react';
23

3-
import { ErrorBoundary } from '../src/errorboundary';
4+
import { ErrorBoundary, ErrorBoundaryProps } from '../src/errorboundary';
45

56
describe('ErrorBoundary', () => {
6-
it('Does not fail', () => {
7-
function Bomb() {
8-
return <h1>{new Error('💥 CABOOM 💥')}</h1>;
7+
const DEFAULT_PROPS: ErrorBoundaryProps = {
8+
fallback: <h1>Error Component</h1>,
9+
fallbackRender: (error: Error, componentStack: string, resetErrorBoundary: () => void) => (
10+
<React.Fragment>
11+
<h1>{error.toString()}</h1>
12+
<h2>{componentStack}</h2>
13+
<button onClick={resetErrorBoundary} />
14+
</React.Fragment>
15+
),
16+
onError: jest.fn(),
17+
onReset: jest.fn(),
18+
};
19+
20+
it('Renders children with no failure', () => {
21+
function Bomb(): JSX.Element {
22+
return <p>Testing children</p>;
923
}
1024

11-
create(
25+
render(
1226
<ErrorBoundary>
1327
<Bomb />
1428
</ErrorBoundary>,

packages/react/tslint.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"dev"
77
],
88
"variable-name": false,
9-
"completed-docs": false
9+
"completed-docs": false,
10+
"interface-over-type-literal": false
1011
}
1112
}

0 commit comments

Comments
 (0)