Skip to content

Commit e62a8d7

Browse files
author
Brian Vaughn
authored
Store throws a specific Error type (UnsupportedBridgeOperationError) (#24147)
When this Error type is detected, DevTools shows a custom error overlay with upgrade/downgrade instructions.
1 parent 3f89908 commit e62a8d7

File tree

5 files changed

+104
-2
lines changed

5 files changed

+104
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export default class UnsupportedBridgeOperationError extends Error {
11+
constructor(message: string) {
12+
super(message);
13+
14+
// Maintains proper stack trace for where our error was thrown (only available on V8)
15+
if (Error.captureStackTrace) {
16+
Error.captureStackTrace(this, UnsupportedBridgeOperationError);
17+
}
18+
19+
this.name = 'UnsupportedBridgeOperationError';
20+
}
21+
}

packages/react-devtools-shared/src/devtools/store.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import type {
4444
FrontendBridge,
4545
BridgeProtocol,
4646
} from 'react-devtools-shared/src/bridge';
47+
import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError';
4748

4849
const debug = (methodName, ...args) => {
4950
if (__DEBUG__) {
@@ -1252,7 +1253,9 @@ export default class Store extends EventEmitter<{|
12521253
break;
12531254
default:
12541255
this._throwAndEmitError(
1255-
Error(`Unsupported Bridge operation "${operation}"`),
1256+
new UnsupportedBridgeOperationError(
1257+
`Unsupported Bridge operation "${operation}"`,
1258+
),
12561259
);
12571260
}
12581261
}

packages/react-devtools-shared/src/devtools/views/ErrorBoundary/ErrorBoundary.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
import * as React from 'react';
1111
import {Component, Suspense} from 'react';
1212
import Store from 'react-devtools-shared/src/devtools/store';
13+
import UnsupportedBridgeOperationView from './UnsupportedBridgeOperationView';
1314
import ErrorView from './ErrorView';
1415
import SearchingGitHubIssues from './SearchingGitHubIssues';
1516
import SuspendingErrorView from './SuspendingErrorView';
1617
import TimeoutView from './TimeoutView';
18+
import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError';
1719
import TimeoutError from 'react-devtools-shared/src/TimeoutError';
1820
import {logEvent} from 'react-devtools-shared/src/Logger';
1921

@@ -30,6 +32,7 @@ type State = {|
3032
componentStack: string | null,
3133
errorMessage: string | null,
3234
hasError: boolean,
35+
isUnsupportedBridgeOperationError: boolean,
3336
isTimeout: boolean,
3437
|};
3538

@@ -39,6 +42,7 @@ const InitialState: State = {
3942
componentStack: null,
4043
errorMessage: null,
4144
hasError: false,
45+
isUnsupportedBridgeOperationError: false,
4246
isTimeout: false,
4347
};
4448

@@ -54,6 +58,8 @@ export default class ErrorBoundary extends Component<Props, State> {
5458
: null;
5559

5660
const isTimeout = error instanceof TimeoutError;
61+
const isUnsupportedBridgeOperationError =
62+
error instanceof UnsupportedBridgeOperationError;
5763

5864
const callStack =
5965
typeof error === 'object' &&
@@ -69,6 +75,7 @@ export default class ErrorBoundary extends Component<Props, State> {
6975
callStack,
7076
errorMessage,
7177
hasError: true,
78+
isUnsupportedBridgeOperationError,
7279
isTimeout,
7380
};
7481
}
@@ -102,6 +109,7 @@ export default class ErrorBoundary extends Component<Props, State> {
102109
componentStack,
103110
errorMessage,
104111
hasError,
112+
isUnsupportedBridgeOperationError,
105113
isTimeout,
106114
} = this.state;
107115

@@ -117,6 +125,14 @@ export default class ErrorBoundary extends Component<Props, State> {
117125
errorMessage={errorMessage}
118126
/>
119127
);
128+
} else if (isUnsupportedBridgeOperationError) {
129+
return (
130+
<UnsupportedBridgeOperationView
131+
callStack={callStack}
132+
componentStack={componentStack}
133+
errorMessage={errorMessage}
134+
/>
135+
);
120136
} else {
121137
return (
122138
<ErrorView
@@ -125,7 +141,10 @@ export default class ErrorBoundary extends Component<Props, State> {
125141
dismissError={
126142
canDismissProp || canDismissState ? this._dismissError : null
127143
}
128-
errorMessage={errorMessage}>
144+
errorMessage={errorMessage}
145+
isUnsupportedBridgeOperationError={
146+
isUnsupportedBridgeOperationError
147+
}>
129148
<Suspense fallback={<SearchingGitHubIssues />}>
130149
<SuspendingErrorView
131150
callStack={callStack}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import * as React from 'react';
11+
import styles from './shared.css';
12+
13+
type Props = {|
14+
callStack: string | null,
15+
children: React$Node,
16+
componentStack: string | null,
17+
errorMessage: string | null,
18+
|};
19+
20+
export default function UnsupportedBridgeOperationView({
21+
callStack,
22+
children,
23+
componentStack,
24+
errorMessage,
25+
}: Props) {
26+
return (
27+
<div className={styles.ErrorBoundary}>
28+
{children}
29+
<div className={styles.ErrorInfo}>
30+
<div className={styles.HeaderRow}>
31+
<div className={styles.ErrorHeader}>
32+
{errorMessage || 'Bridge protocol mismatch'}
33+
</div>
34+
</div>
35+
<div className={styles.InfoBox}>
36+
An incompatible version of <code>react-devtools-core</code> has been
37+
embedded in a renderer like React Native. To fix this, update the{' '}
38+
<code>react-devtools-core</code> package within the React Native
39+
application, or downgrade the <code>react-devtools</code> package you
40+
use to open the DevTools UI.
41+
</div>
42+
{!!callStack && (
43+
<div className={styles.ErrorStack}>
44+
The error was thrown {callStack.trim()}
45+
</div>
46+
)}
47+
</div>
48+
</div>
49+
);
50+
}

packages/react-devtools-shared/src/devtools/views/ErrorBoundary/shared.css

+9
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,13 @@
121121

122122
.CloseButtonIcon {
123123
margin-left: 0.25rem;
124+
}
125+
126+
.InfoBox {
127+
margin-top: 0.5rem;
128+
background: var(--color-console-warning-background);
129+
border: 1px solid var(--color-console-warning-border);
130+
padding: 0.25rem 0.5rem;
131+
border-radius: 0.5rem;
132+
color: var(--color-console-warning-text);
124133
}

0 commit comments

Comments
 (0)