fix[devtools/inspectElement]: dont pause initial inspectElement call when user switches tabs (#27488)

There are not so many changes, most of them are changing imports,
because I've moved types for UI in a single file.

In https://github.com/facebook/react/pull/27357 I've added support for
pausing polling events: when user inspects an element, we start polling
React DevTools backend for updates in props / state. If user switches
tabs, extension's service worker can be killed by browser and this
polling will start spamming errors.

What I've missed is that we also have a separate call for this API, but
which is executed only once when user selects an element. We don't
handle promise rejection here and this can lead to some errors when user
selects an element and switches tabs right after it.

The only change here is that this API now has
`shouldListenToPauseEvents` param, which is `true` for polling, so we
will pause polling once user switches tabs. It is `false` by default, so
we won't pause initial call by accident.


af8beeebf6/packages/react-devtools-shared/src/backendAPI.js (L96)
This commit is contained in:
Ruslan Lesiutin
2023-10-10 18:10:17 +01:00
committed by GitHub
parent 151e75a128
commit 77ec61885f
58 changed files with 305 additions and 282 deletions

View File

@@ -21,7 +21,7 @@ import {
TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
} from '../constants';
import {ElementTypeRoot} from '../types';
import {ElementTypeRoot} from '../frontend/types';
import {
getSavedComponentFilters,
setSavedComponentFilters,
@@ -37,10 +37,13 @@ import {
BRIDGE_PROTOCOL,
currentBridgeProtocol,
} from 'react-devtools-shared/src/bridge';
import {StrictMode} from 'react-devtools-shared/src/types';
import {StrictMode} from 'react-devtools-shared/src/frontend/types';
import type {Element} from './views/Components/types';
import type {ComponentFilter, ElementType} from '../types';
import type {
Element,
ComponentFilter,
ElementType,
} from 'react-devtools-shared/src/frontend/types';
import type {
FrontendBridge,
BridgeProtocol,

View File

@@ -9,7 +9,7 @@
import JSON5 from 'json5';
import type {Element} from './views/Components/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
import type {StateContext} from './views/Components/TreeContext';
import type Store from './store';

View File

@@ -11,7 +11,7 @@ import * as React from 'react';
import {Fragment} from 'react';
import styles from './Badge.css';
import type {ElementType} from 'react-devtools-shared/src/types';
import type {ElementType} from 'react-devtools-shared/src/frontend/types';
type Props = {
className?: string,

View File

@@ -13,7 +13,7 @@ import {StoreContext} from '../context';
import {
ComponentFilterElementType,
ElementTypeSuspense,
} from 'react-devtools-shared/src/types';
} from 'react-devtools-shared/src/frontend/types';
export default function CannotSuspendWarningMessage(): React.Node {
const store = useContext(StoreContext);

View File

@@ -20,7 +20,7 @@ import {useSubscription} from '../hooks';
import {logEvent} from 'react-devtools-shared/src/Logger';
import type {ItemData} from './Tree';
import type {Element as ElementType} from './types';
import type {Element as ElementType} from 'react-devtools-shared/src/frontend/types';
import styles from './Element.css';
import Icon from '../Icon';

View File

@@ -10,7 +10,7 @@
import * as React from 'react';
import styles from './HocBadges.css';
import type {Element} from './types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
type Props = {
element: Element,

View File

@@ -17,7 +17,7 @@ import Icon from '../Icon';
import {ModalDialogContext} from '../ModalDialog';
import ViewElementSourceContext from './ViewElementSourceContext';
import Toggle from '../Toggle';
import {ElementTypeSuspense} from 'react-devtools-shared/src/types';
import {ElementTypeSuspense} from 'react-devtools-shared/src/frontend/types';
import CannotSuspendWarningMessage from './CannotSuspendWarningMessage';
import InspectedElementView from './InspectedElementView';
import {InspectedElementContext} from './InspectedElementContext';
@@ -26,7 +26,7 @@ import {LOCAL_STORAGE_OPEN_IN_EDITOR_URL} from '../../../constants';
import styles from './InspectedElement.css';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
export type Props = {};

View File

@@ -37,12 +37,12 @@ import FetchFileWithCachingContext from 'react-devtools-shared/src/devtools/view
import HookNamesModuleLoaderContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
import {SettingsContext} from '../Settings/SettingsContext';
import type {HookNames} from 'react-devtools-shared/src/types';
import type {HookNames} from 'react-devtools-shared/src/frontend/types';
import type {ReactNodeList} from 'shared/ReactTypes';
import type {
Element,
InspectedElement,
} from 'react-devtools-shared/src/devtools/views/Components/types';
} from 'react-devtools-shared/src/frontend/types';
type Path = Array<string | number>;
type InspectPathFunction = (path: Path) => void;

View File

@@ -18,11 +18,11 @@ import styles from './InspectedElementSharedStyles.css';
import {
ElementTypeClass,
ElementTypeFunction,
} from 'react-devtools-shared/src/types';
} from 'react-devtools-shared/src/frontend/types';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
type Props = {
bridge: FrontendBridge,

View File

@@ -24,7 +24,7 @@ import {
clearWarningsForElement as clearWarningsForElementAPI,
} from 'react-devtools-shared/src/backendAPI';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
type Props = {

View File

@@ -25,11 +25,11 @@ import {getHookSourceLocationKey} from 'react-devtools-shared/src/hookNamesCache
import HookNamesModuleLoaderContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
import isArray from 'react-devtools-shared/src/isArray';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {HookNames} from 'react-devtools-shared/src/types';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {HookNames} from 'react-devtools-shared/src/frontend/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
import type {ToggleParseHookNames} from './InspectedElementContext';
type HooksTreeViewProps = {

View File

@@ -17,11 +17,11 @@ import NewKeyValue from './NewKeyValue';
import {alphaSortEntries, serializeDataForCopy} from '../utils';
import Store from '../../store';
import styles from './InspectedElementSharedStyles.css';
import {ElementTypeClass} from 'react-devtools-shared/src/types';
import {ElementTypeClass} from 'react-devtools-shared/src/frontend/types';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
type Props = {
bridge: FrontendBridge,

View File

@@ -9,7 +9,7 @@
import {copy} from 'clipboard-js';
import * as React from 'react';
import {ElementTypeHostComponent} from 'react-devtools-shared/src/types';
import {ElementTypeHostComponent} from 'react-devtools-shared/src/frontend/types';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import KeyValue from './KeyValue';
@@ -17,9 +17,9 @@ import {alphaSortEntries, serializeDataForCopy} from '../utils';
import Store from '../../store';
import styles from './InspectedElementSharedStyles.css';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
type Props = {
bridge: FrontendBridge,

View File

@@ -14,9 +14,9 @@ import sharedStyles from './InspectedElementSharedStyles.css';
import styles from './InspectedElementStyleXPlugin.css';
import {enableStyleXFeatures} from 'react-devtools-feature-flags';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
type Props = {
bridge: FrontendBridge,

View File

@@ -11,10 +11,10 @@ import * as React from 'react';
import {OptionsContext} from '../context';
import EditableValue from './EditableValue';
import Store from '../../store';
import {ElementTypeSuspense} from 'react-devtools-shared/src/types';
import {ElementTypeSuspense} from 'react-devtools-shared/src/frontend/types';
import styles from './InspectedElementSharedStyles.css';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
type Props = {

View File

@@ -38,8 +38,15 @@ import {logEvent} from 'react-devtools-shared/src/Logger';
import styles from './InspectedElementView.css';
import type {ContextMenuContextType} from '../context';
import type {Element, InspectedElement, SerializedElement} from './types';
import type {ElementType, HookNames} from 'react-devtools-shared/src/types';
import type {
Element,
InspectedElement,
SerializedElement,
} from 'react-devtools-shared/src/frontend/types';
import type {
ElementType,
HookNames,
} from 'react-devtools-shared/src/frontend/types';
import type {ToggleParseHookNames} from './InspectedElementContext';
export type CopyPath = (path: Array<string | number>) => void;

View File

@@ -28,8 +28,8 @@ import isArray from 'react-devtools-shared/src/isArray';
import {InspectedElementContext} from './InspectedElementContext';
import {PROTOCOLS_SUPPORTED_AS_LINKS_IN_KEY_VALUE} from './constants';
import type {InspectedElement} from './types';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
import type {Element as ReactElement} from 'react';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';

View File

@@ -31,7 +31,7 @@ import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type Store from 'react-devtools-shared/src/devtools/store';
import type {StyleAndLayout as StyleAndLayoutBackend} from 'react-devtools-shared/src/backend/NativeStyleEditor/types';
import type {StyleAndLayout as StyleAndLayoutFrontend} from './types';
import type {Element} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
import type {
Resource,
Thenable,

View File

@@ -15,7 +15,7 @@ import {smartParse} from '../../utils';
import {parseHookPathForEdit} from './utils';
import styles from './NewArrayValue.css';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
type Props = {

View File

@@ -15,7 +15,7 @@ import EditableValue from './EditableValue';
import {parseHookPathForEdit} from './utils';
import styles from './NewKeyValue.css';
import type {InspectedElement} from './types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
type Props = {

View File

@@ -20,7 +20,7 @@ import type {OwnersList} from 'react-devtools-shared/src/backend/types';
import type {
Element,
SerializedElement,
} from 'react-devtools-shared/src/devtools/views/Components/types';
} from 'react-devtools-shared/src/frontend/types';
import type {Resource, Thenable} from '../../cache';
type Context = (id: number) => Array<SerializedElement> | null;

View File

@@ -32,7 +32,7 @@ import {
MenuItem,
} from '../Components/reach-ui/menu-button';
import type {SerializedElement} from './types';
import type {SerializedElement} from 'react-devtools-shared/src/frontend/types';
import styles from './OwnersStack.css';

View File

@@ -7,7 +7,8 @@
* @flow
*/
import type {Element} from './types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
import * as React from 'react';
import {useContext, useMemo} from 'react';
import {TreeStateContext} from './TreeContext';

View File

@@ -42,7 +42,7 @@ import {createRegExp} from '../utils';
import {BridgeContext, StoreContext} from '../context';
import Store from '../../store';
import type {Element} from './types';
import type {Element} from 'react-devtools-shared/src/frontend/types';
export type StateContext = {
// Tree

View File

@@ -1,140 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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 type {Source} from 'shared/ReactElementType';
import type {
Dehydrated,
Unserializable,
} from 'react-devtools-shared/src/hydration';
import type {ElementType, Plugins} from 'react-devtools-shared/src/types';
// Each element on the frontend corresponds to a Fiber on the backend.
// Some of its information (e.g. id, type, displayName) come from the backend.
// Other bits (e.g. weight and depth) are computed on the frontend for windowing and display purposes.
// Elements are updated on a push basis meaning the backend pushes updates to the frontend when needed.
export type Element = {
id: number,
parentID: number,
children: Array<number>,
type: ElementType,
displayName: string | null,
key: number | string | null,
hocDisplayNames: null | Array<string>,
// Should the elements children be visible in the tree?
isCollapsed: boolean,
// Owner (if available)
ownerID: number,
// How many levels deep within the tree is this element?
// This determines how much indentation (left padding) should be used in the Elements tree.
depth: number,
// How many nodes (including itself) are below this Element within the tree.
// This property is used to quickly determine the total number of Elements,
// and the Element at any given index (for windowing purposes).
weight: number,
// This element is not in a StrictMode compliant subtree.
// Only true for React versions supporting StrictMode.
isStrictModeNonCompliant: boolean,
};
export type SerializedElement = {
displayName: string | null,
id: number,
key: number | string | null,
hocDisplayNames: Array<string> | null,
type: ElementType,
};
export type OwnersList = {
id: number,
owners: Array<SerializedElement> | null,
};
export type InspectedElementResponseType =
| 'error'
| 'full-data'
| 'hydrated-path'
| 'no-change'
| 'not-found';
export type InspectedElement = {
id: number,
// Does the current renderer support editable hooks and function props?
canEditHooks: boolean,
canEditFunctionProps: boolean,
// Does the current renderer support advanced editing interface?
canEditHooksAndDeletePaths: boolean,
canEditHooksAndRenamePaths: boolean,
canEditFunctionPropsDeletePaths: boolean,
canEditFunctionPropsRenamePaths: boolean,
// Is this Error, and can its value be overridden now?
isErrored: boolean,
canToggleError: boolean,
targetErrorBoundaryID: ?number,
// Is this Suspense, and can its value be overridden now?
canToggleSuspense: boolean,
// Can view component source location.
canViewSource: boolean,
// Does the component have legacy context attached to it.
hasLegacyContext: boolean,
// Inspectable properties.
context: Object | null,
hooks: Object | null,
props: Object | null,
state: Object | null,
key: number | string | null,
errors: Array<[string, number]>,
warnings: Array<[string, number]>,
// List of owners
owners: Array<SerializedElement> | null,
// Location of component in source code.
source: Source | null,
type: ElementType,
// Meta information about the root this element belongs to.
rootType: string | null,
// Meta information about the renderer that created this element.
rendererPackageName: string | null,
rendererVersion: string | null,
// UI plugins/visualizations for the inspected element.
plugins: Plugins,
};
// TODO: Add profiling type
type Data =
| string
| Dehydrated
| Unserializable
| Array<Dehydrated>
| Array<Unserializable>
| {[string]: Data};
export type DehydratedData = {
cleaned: Array<Array<string | number>>,
data: Data,
unserializable: Array<Array<string | number>>,
};

View File

@@ -46,11 +46,11 @@ import styles from './DevTools.css';
import './root.css';
import type {InspectedElement} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
import type {FetchFileWithCaching} from './Components/FetchFileWithCachingContext';
import type {HookNamesModuleLoaderFunction} from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {BrowserTheme} from 'react-devtools-shared/src/types';
import type {BrowserTheme} from 'react-devtools-shared/src/frontend/types';
export type TabID = 'components' | 'profiler';

View File

@@ -18,10 +18,10 @@ import {
TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
} from 'react-devtools-shared/src/constants';
import {utfDecodeString} from 'react-devtools-shared/src/utils';
import {ElementTypeRoot} from 'react-devtools-shared/src/types';
import {ElementTypeRoot} from 'react-devtools-shared/src/frontend/types';
import ProfilerStore from 'react-devtools-shared/src/devtools/ProfilerStore';
import type {ElementType} from 'react-devtools-shared/src/types';
import type {ElementType} from 'react-devtools-shared/src/frontend/types';
import type {
CommitTree,
CommitTreeNode,

View File

@@ -10,7 +10,7 @@
import {
ElementTypeForwardRef,
ElementTypeMemo,
} from 'react-devtools-shared/src/types';
} from 'react-devtools-shared/src/frontend/types';
import {formatDuration} from './utils';
import ProfilerStore from 'react-devtools-shared/src/devtools/ProfilerStore';

View File

@@ -8,13 +8,13 @@
*/
import type {CommitTree} from './types';
import type {SerializedElement} from '../Components/types';
import type {SerializedElement} from 'react-devtools-shared/src/frontend/types';
import * as React from 'react';
import {useContext} from 'react';
import {ProfilerContext} from './ProfilerContext';
import styles from './Updaters.css';
import {ElementTypeRoot} from '../../../types';
import {ElementTypeRoot} from '../../../frontend/types';
export type Props = {
commitTree: CommitTree,

View File

@@ -7,8 +7,10 @@
* @flow
*/
import type {ElementType} from 'react-devtools-shared/src/types';
import type {SerializedElement} from '../Components/types';
import type {
ElementType,
SerializedElement,
} from 'react-devtools-shared/src/frontend/types';
import type {
TimelineData,
TimelineDataExport,

View File

@@ -40,7 +40,7 @@ import {
ElementTypeOtherOrUnknown,
ElementTypeProfiler,
ElementTypeSuspense,
} from 'react-devtools-shared/src/types';
} from 'react-devtools-shared/src/frontend/types';
import {getDefaultOpenInEditorURL} from 'react-devtools-shared/src/utils';
import styles from './SettingsShared.css';
@@ -52,7 +52,7 @@ import type {
ElementType,
ElementTypeComponentFilter,
RegExpComponentFilter,
} from 'react-devtools-shared/src/types';
} from 'react-devtools-shared/src/frontend/types';
const vscodeFilepath = 'vscode://file/{path}:{line}';

View File

@@ -34,7 +34,7 @@ import {useLocalStorage} from '../hooks';
import {BridgeContext} from '../context';
import {logEvent} from 'react-devtools-shared/src/Logger';
import type {BrowserTheme} from 'react-devtools-shared/src/types';
import type {BrowserTheme} from 'react-devtools-shared/src/frontend/types';
export type DisplayDensity = 'comfortable' | 'compact';
export type Theme = 'auto' | 'light' | 'dark';