@samitouri / QOS-React / commits / 99995c1063

Improve error boundary in inspected elements panel (#21531)

Show more info about the error as well as the option to report it to GitHub.

Brian Vaughn committed May 19, 2021 at 10:27 UTC 99995c1063d1a9d3bfbcdf41078cb1e685218d99
3 files changed +21 -97
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementErrorBoundary.css
+2 -20
@@ -1,21 +1,3 @@
1 -.Error {
2 - justify-content: center;
3 - align-items: center;
4 - display: flex;
5 - flex-direction: column;
6 - height: 100%;
7 - font-size: var(--font-size-sans-large);
8 - font-weight: bold;
9 - text-align: center;
10 - background-color: var(--color-error-background);
11 - color: var(--color-error-text);
12 - border: 1px solid var(--color-error-border);
13 - padding: 1rem;
14 -}
15 -
16 -.Message {
17 - margin-bottom: 1rem;
18 -}
19 -
20 -.RetryButton {
1 +.Wrapper {
2 + border-left: 1px solid var(--color-border);
3 }
\ No newline at end of file
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementErrorBoundary.js
+4 -71
@@ -8,13 +8,9 @@
8 */
9
10 import * as React from 'react';
11 -import {Component, useContext} from 'react';
12 -import {TreeDispatcherContext} from './TreeContext';
13 -import Button from 'react-devtools-shared/src/devtools/views/Button';
11 +import ErrorBoundary from '../ErrorBoundary';
12 import styles from './InspectedElementErrorBoundary.css';
13
16 -import type {DispatcherContext} from './InspectedElementErrorBoundary.css';
17 -
14 type WrapperProps = {|
15 children: React$Node,
16 |};
@@ -22,72 +18,9 @@ type WrapperProps = {|
18 export default function InspectedElementErrorBoundaryWrapper({
19 children,
20 }: WrapperProps) {
25 - const dispatch = useContext(TreeDispatcherContext);
26 -
21 return (
28 - <InspectedElementErrorBoundary children={children} dispatch={dispatch} />
22 + <div className={styles.Wrapper}>
23 + <ErrorBoundary canDismiss={true}>{children}</ErrorBoundary>
24 + </div>
25 );
26 }
31 -
32 -type Props = {|
33 - children: React$Node,
34 - dispatch: DispatcherContext,
35 -|};
36 -
37 -type State = {|
38 - errorMessage: string | null,
39 - hasError: boolean,
40 -|};
41 -
42 -const InitialState: State = {
43 - errorMessage: null,
44 - hasError: false,
45 -};
46 -
47 -class InspectedElementErrorBoundary extends Component<Props, State> {
48 - state: State = InitialState;
49 -
50 - static getDerivedStateFromError(error: any) {
51 - const errorMessage =
52 - typeof error === 'object' &&
53 - error !== null &&
54 - error.hasOwnProperty('message')
55 - ? error.message
56 - : error;
57 -
58 - return {
59 - errorMessage,
60 - hasError: true,
61 - };
62 - }
63 -
64 - render() {
65 - const {children} = this.props;
66 - const {errorMessage, hasError} = this.state;
67 -
68 - if (hasError) {
69 - return (
70 - <div className={styles.Error}>
71 - <div className={styles.Message}>{errorMessage || 'Error'}</div>
72 - <Button className={styles.RetryButton} onClick={this._retry}>
73 - Dismiss
74 - </Button>
75 - </div>
76 - );
77 - }
78 -
79 - return children;
80 - }
81 -
82 - _retry = () => {
83 - const {dispatch} = this.props;
84 - dispatch({
85 - type: 'SELECT_ELEMENT_BY_ID',
86 - payload: null,
87 - });
88 - this.setState({
89 - errorMessage: null,
90 - hasError: false,
91 - });
92 - };
93 -}
packages/react-devtools-shared/src/devtools/views/ErrorBoundary/ErrorBoundary.js
+15 -6
@@ -16,7 +16,8 @@ import SuspendingErrorView from './SuspendingErrorView';
16
17 type Props = {|
18 children: React$Node,
19 - store: Store,
19 + canDismiss?: boolean,
20 + store?: Store,
21 |};
22
23 type State = {|
@@ -70,18 +71,24 @@ export default class ErrorBoundary extends Component<Props, State> {
71 }
72
73 componentDidMount() {
73 - this.props.store.addListener('error', this._onStoreError);
74 + const {store} = this.props;
75 + if (store != null) {
76 + store.addListener('error', this._onStoreError);
77 + }
78 }
79
80 componentWillUnmount() {
77 - this.props.store.removeListener('error', this._onStoreError);
81 + const {store} = this.props;
82 + if (store != null) {
83 + store.removeListener('error', this._onStoreError);
84 + }
85 }
86
87 render() {
81 - const {children} = this.props;
88 + const {canDismiss: canDismissProp, children} = this.props;
89 const {
90 callStack,
84 - canDismiss,
91 + canDismiss: canDismissState,
92 componentStack,
93 errorMessage,
94 hasError,
@@ -92,7 +99,9 @@ export default class ErrorBoundary extends Component<Props, State> {
99 <ErrorView
100 callStack={callStack}
101 componentStack={componentStack}
95 - dismissError={canDismiss ? this._dismissError : null}
102 + dismissError={
103 + canDismissProp || canDismissState ? this._dismissError : null
104 + }
105 errorMessage={errorMessage}>
106 <Suspense fallback={<SearchingGitHubIssues />}>
107 <SuspendingErrorView