diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js
index 10aa4a0dcb..6fd24e689f 100644
--- a/packages/react-devtools-shared/src/backend/renderer.js
+++ b/packages/react-devtools-shared/src/backend/renderer.js
@@ -60,7 +60,10 @@ import {
TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
} from '../constants';
-import {inspectHooksOfFiber, ErrorsNames as DebugToolsErrors} from 'react-debug-tools';
+import {
+ inspectHooksOfFiber,
+ ErrorsNames as DebugToolsErrors,
+} from 'react-debug-tools';
import {
patch as patchConsole,
registerRenderer as registerRendererWithConsole,
@@ -3619,9 +3622,13 @@ export function attach(
if (error.name === DebugToolsErrors.RENDER_FUNCTION_ERROR) {
let message = 'Error rendering inspected element.';
let stack;
+ // Log error & cause for user to debug
console.error(message + '\n\n', error);
if (error.cause != null) {
- console.error('Original error causing above error: \n\n', error.cause);
+ console.error(
+ 'Original error causing above error: \n\n',
+ error.cause,
+ );
if (error.cause instanceof Error) {
message = error.cause.message || message;
stack = error.cause.stack;
@@ -3637,6 +3644,16 @@ export function attach(
};
}
+ if (error.name === DebugToolsErrors.UNSUPPORTTED_FEATURE_ERROR) {
+ return {
+ type: 'unsupported-feature',
+ id,
+ responseID: requestID,
+ message: 'Unsupported feature: ' + error.message,
+ };
+ }
+
+ // Log Uncaught Error
console.error('Error inspecting element.\n\n', error);
return {
diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js
index 11019d3f0e..4a263baa52 100644
--- a/packages/react-devtools-shared/src/backend/types.js
+++ b/packages/react-devtools-shared/src/backend/types.js
@@ -282,6 +282,7 @@ export type InspectedElement = {|
export const InspectElementErrorType = 'error';
export const InspectElementUserErrorType = 'user-error';
+export const InspectElementUnsupportedFeatureErrorType = 'unsupported-feature';
export const InspectElementFullDataType = 'full-data';
export const InspectElementNoChangeType = 'no-change';
export const InspectElementNotFoundType = 'not-found';
@@ -302,6 +303,13 @@ export type InspectElementUserError = {|
stack: ?string,
|};
+export type InspectElementUnsupportedFeatureError = {|
+ id: number,
+ responseID: number,
+ type: 'unsupported-feature',
+ message: string,
+|};
+
export type InspectElementFullData = {|
id: number,
responseID: number,
@@ -332,6 +340,7 @@ export type InspectElementNotFound = {|
export type InspectedElementPayload =
| InspectElementError
| InspectElementUserError
+ | InspectElementUnsupportedFeatureError
| InspectElementFullData
| InspectElementHydratedPath
| InspectElementNoChange
diff --git a/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/CaughtErrorView.js b/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/CaughtErrorView.js
new file mode 100644
index 0000000000..c4511ba031
--- /dev/null
+++ b/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/CaughtErrorView.js
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import * as React from 'react';
+import styles from './shared.css';
+
+type Props = {|
+ callStack: string | null,
+ children: React$Node,
+ info: React$Node | null,
+ componentStack: string | null,
+ errorMessage: string,
+|};
+
+export default function CaughtErrorView({
+ callStack,
+ children,
+ info,
+ componentStack,
+ errorMessage,
+}: Props) {
+ return (
+
+ {children}
+
+
+ {!!info &&
{info}
}
+ {!!callStack && (
+
+ The error was thrown {callStack.trim()}
+
+ )}
+
+
+ );
+}
diff --git a/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/ErrorBoundary.js b/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/ErrorBoundary.js
index c770ce1b43..694e0ca205 100644
--- a/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/ErrorBoundary.js
+++ b/packages/react-devtools-shared/src/devtools/views/ErrorBoundary/ErrorBoundary.js
@@ -15,11 +15,13 @@ import ErrorView from './ErrorView';
import SearchingGitHubIssues from './SearchingGitHubIssues';
import SuspendingErrorView from './SuspendingErrorView';
import TimeoutView from './TimeoutView';
-import UserErrorView from './UserErrorView';
+import CaughtErrorView from './CaughtErrorView';
import UnsupportedBridgeOperationError from 'react-devtools-shared/src/UnsupportedBridgeOperationError';
import TimeoutError from 'react-devtools-shared/src/errors/TimeoutError';
import UserError from 'react-devtools-shared/src/errors/UserError';
+import UnsupportedFeatureError from 'react-devtools-shared/src/errors/UnsupportedFeatureError';
import {logEvent} from 'react-devtools-shared/src/Logger';
+import {boolean} from 'yargs';
type Props = {|
children: React$Node,
@@ -37,6 +39,7 @@ type State = {|
isUnsupportedBridgeOperationError: boolean,
isTimeout: boolean,
isUserError: boolean,
+ isUnsupportedFeatureError: boolean,
|};
const InitialState: State = {
@@ -48,6 +51,7 @@ const InitialState: State = {
isUnsupportedBridgeOperationError: false,
isTimeout: false,
isUserError: false,
+ isUnsupportedFeatureError: false,
};
export default class ErrorBoundary extends Component {
@@ -63,6 +67,7 @@ export default class ErrorBoundary extends Component {
const isTimeout = error instanceof TimeoutError;
const isUserError = error instanceof UserError;
+ const isUnsupportedFeatureError = error instanceof UnsupportedFeatureError;
const isUnsupportedBridgeOperationError =
error instanceof UnsupportedBridgeOperationError;
@@ -81,6 +86,7 @@ export default class ErrorBoundary extends Component {
errorMessage,
hasError: true,
isUnsupportedBridgeOperationError,
+ isUnsupportedFeatureError,
isTimeout,
isUserError,
};
@@ -118,6 +124,7 @@ export default class ErrorBoundary extends Component {
isUnsupportedBridgeOperationError,
isTimeout,
isUserError,
+ isUnsupportedFeatureError,
} = this.state;
if (hasError) {
@@ -142,10 +149,34 @@ export default class ErrorBoundary extends Component {
);
} else if (isUserError) {
return (
-
+ This is likely to be caused by implementation of current
+ inspected element. Please see your console for logged error.
+ >
+ }
+ />
+ );
+ } else if (isUnsupportedFeatureError) {
+ return (
+
+ React DevTools is unable to handle a feature you are using in
+ this component (e.g. a new React build-in Hook). Please upgrade
+ to the latest version.
+ >
+ }
/>
);
} else {
@@ -156,10 +187,7 @@ export default class ErrorBoundary extends Component {
dismissError={
canDismissProp || canDismissState ? this._dismissError : null
}
- errorMessage={errorMessage}
- isUnsupportedBridgeOperationError={
- isUnsupportedBridgeOperationError
- }>
+ errorMessage={errorMessage}>
}>
- {children}
-
-
-
- {errorMessage || 'Error occured in inspected element'}
-
-
-
- This is likely to be caused by implementation of current inspected element.
-
- {!!callStack && (
-
- The error was thrown {callStack.trim()}
-
- )}
-
-
- );
- }
-
\ No newline at end of file
diff --git a/packages/react-devtools-shared/src/errors/UnsupportedFeatureError.js b/packages/react-devtools-shared/src/errors/UnsupportedFeatureError.js
new file mode 100644
index 0000000000..0f98b0b376
--- /dev/null
+++ b/packages/react-devtools-shared/src/errors/UnsupportedFeatureError.js
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+export default class UnsupportedFeatureError extends Error {
+ constructor(message: string) {
+ super(message);
+
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, UnsupportedFeatureError);
+ }
+
+ this.name = 'UnsupportedFeatureError';
+ }
+}
diff --git a/packages/react-devtools-shared/src/errors/UserError.js b/packages/react-devtools-shared/src/errors/UserError.js
index 752dbf9e88..81a9ee86e5 100644
--- a/packages/react-devtools-shared/src/errors/UserError.js
+++ b/packages/react-devtools-shared/src/errors/UserError.js
@@ -7,7 +7,7 @@
* @flow
*/
- export default class UserError extends Error {
+export default class UserError extends Error {
constructor(message: string) {
super(message);
diff --git a/packages/react-devtools-shared/src/inspectedElementMutableSource.js b/packages/react-devtools-shared/src/inspectedElementMutableSource.js
index 329f174f15..4396d27d63 100644
--- a/packages/react-devtools-shared/src/inspectedElementMutableSource.js
+++ b/packages/react-devtools-shared/src/inspectedElementMutableSource.js
@@ -21,6 +21,7 @@ import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {
InspectElementError,
InspectElementUserError,
+ InspectElementUnsupportedFeatureError,
InspectElementFullData,
InspectElementHydratedPath,
} from 'react-devtools-shared/src/backend/types';
@@ -30,6 +31,7 @@ import type {
InspectedElementResponseType,
} from 'react-devtools-shared/src/devtools/views/Components/types';
import UserError from 'react-devtools-shared/src/errors/UserError';
+import UnsupportedFeatureError from 'react-devtools-shared/src/errors/UnsupportedFeatureError';
// Maps element ID to inspected data.
// We use an LRU for this rather than a WeakMap because of how the "no-change" optimization works.
@@ -101,6 +103,13 @@ export function inspectElement({
throw error;
}
+ case 'unsupported-faeture': {
+ const {message} = (data: InspectElementUnsupportedFeatureError);
+ // Trying to keep useful information from user's side.
+ const error = new UnsupportedFeatureError(message);
+ throw error;
+ }
+
case 'no-change':
// This is a no-op for the purposes of our cache.
inspectedElement = inspectedElementCache.get(id);