Improve Bridge Flow types (#352)

* Updated local fork of react-window
* Updated Fow 97 -> 103
* Lint ignore NPM dist
* Improved Bridge Flow types
This commit is contained in:
Brian Vaughn
2019-07-20 14:08:23 -07:00
committed by GitHub
parent 39ad101ea2
commit 4b34a77d29
46 changed files with 1070 additions and 383 deletions
+5 -5
View File
@@ -15,9 +15,9 @@ describe('Bridge', () => {
const bridge = new Bridge(wall);
// Check that we're wired up correctly.
bridge.send('init');
bridge.send('reloadAppForProfiling');
jest.runAllTimers();
expect(wall.send).toHaveBeenCalledWith('init', undefined, undefined);
expect(wall.send).toHaveBeenCalledWith('reloadAppForProfiling');
// Should flush pending messages and then shut down.
wall.send.mockClear();
@@ -25,9 +25,9 @@ describe('Bridge', () => {
bridge.send('update', '2');
bridge.shutdown();
jest.runAllTimers();
expect(wall.send).toHaveBeenCalledWith('update', '1', undefined);
expect(wall.send).toHaveBeenCalledWith('update', '2', undefined);
expect(wall.send).toHaveBeenCalledWith('shutdown', undefined, undefined);
expect(wall.send).toHaveBeenCalledWith('update', '1');
expect(wall.send).toHaveBeenCalledWith('update', '2');
expect(wall.send).toHaveBeenCalledWith('shutdown');
// Verify that the Bridge doesn't send messages after shutdown.
spyOn(console, 'warn');
@@ -2,14 +2,14 @@
import typeof ReactTestRenderer from 'react-test-renderer';
import type { GetInspectedElementPath } from 'src/devtools/views/Components/InspectedElementContext';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
describe('InspectedElementContext', () => {
let React;
let ReactDOM;
let TestRenderer: ReactTestRenderer;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
let meta;
let utils;
+2 -2
View File
@@ -2,7 +2,7 @@
import type { InspectedElementPayload } from 'src/backend/types';
import type { DehydratedData } from 'src/devtools/views/Components/types';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
describe('InspectedElementContext', () => {
@@ -10,7 +10,7 @@ describe('InspectedElementContext', () => {
let ReactDOM;
let hydrate;
let meta;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
const act = (callback: Function) => {
+2 -2
View File
@@ -2,14 +2,14 @@
import typeof ReactTestRenderer from 'react-test-renderer';
import type { Element } from 'src/devtools/views/Components/types';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
describe('OwnersListContext', () => {
let React;
let ReactDOM;
let TestRenderer: ReactTestRenderer;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
let utils;
+2 -2
View File
@@ -1,7 +1,7 @@
// @flow
import typeof ReactTestRenderer from 'react-test-renderer';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type { Context } from 'src/devtools/views/Profiler/ProfilerContext';
import type { DispatcherContext } from 'src/devtools/views/Components/TreeContext';
import type Store from 'src/devtools/store';
@@ -10,7 +10,7 @@ describe('ProfilerContext', () => {
let React;
let ReactDOM;
let TestRenderer: ReactTestRenderer;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
let utils;
+2 -2
View File
@@ -1,7 +1,7 @@
// @flow
import typeof ReactTestRenderer from 'react-test-renderer';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
describe('ProfilingCache', () => {
@@ -11,7 +11,7 @@ describe('ProfilingCache', () => {
let Scheduler;
let SchedulerTracing;
let TestRenderer: ReactTestRenderer;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
let utils;
+3 -3
View File
@@ -29,7 +29,7 @@ describe('profiling charts', () => {
describe('flamegraph chart', () => {
it('should contain valid data', () => {
const Parent = ({ count }) => {
const Parent = (_: {||}) => {
Scheduler.unstable_advanceTime(10);
return (
<React.Fragment>
@@ -105,7 +105,7 @@ describe('profiling charts', () => {
describe('ranked chart', () => {
it('should contain valid data', () => {
const Parent = ({ count }) => {
const Parent = (_: {||}) => {
Scheduler.unstable_advanceTime(10);
return (
<React.Fragment>
@@ -177,7 +177,7 @@ describe('profiling charts', () => {
describe('interactions', () => {
it('should contain valid data', () => {
const Parent = ({ count }) => {
const Parent = (_: {||}) => {
Scheduler.unstable_advanceTime(10);
return (
<React.Fragment>
+4 -2
View File
@@ -1,5 +1,7 @@
// @flow
import type { BackendBridge, FrontendBridge } from 'src/bridge';
const env = jasmine.getEnv();
env.beforeEach(() => {
// These files should be required (and re-reuired) before each test,
@@ -51,13 +53,13 @@ env.beforeEach(() => {
},
});
const agent = new Agent(bridge);
const agent = new Agent(((bridge: any): BackendBridge));
const hook = global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
initBackend(hook, agent, global);
const store = new Store(bridge);
const store = new Store(((bridge: any): FrontendBridge));
global.agent = agent;
global.bridge = bridge;
+2 -2
View File
@@ -1,6 +1,6 @@
// @flow
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
describe('Store component filters', () => {
@@ -8,7 +8,7 @@ describe('Store component filters', () => {
let ReactDOM;
let TestUtils;
let Types;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
let utils;
+2 -2
View File
@@ -1,7 +1,7 @@
// @flow
import typeof ReactTestRenderer from 'react-test-renderer';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
import type {
DispatcherContext,
@@ -12,7 +12,7 @@ describe('TreeListContext', () => {
let React;
let ReactDOM;
let TestRenderer: ReactTestRenderer;
let bridge: Bridge;
let bridge: FrontendBridge;
let store: Store;
let utils;
+2 -2
View File
@@ -2,7 +2,7 @@
import typeof ReactTestRenderer from 'react-test-renderer';
import type Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
import type Store from 'src/devtools/store';
import type { ProfilingDataFrontend } from 'src/devtools/views/Profiler/types';
import type { ElementType } from 'src/types';
@@ -161,7 +161,7 @@ export function requireTestRenderer(): ReactTestRenderer {
}
}
export function exportImportHelper(bridge: Bridge, store: Store): void {
export function exportImportHelper(bridge: FrontendBridge, store: Store): void {
const { act } = require('./utils');
const {
prepareProfilingDataExport,
@@ -1,16 +1,16 @@
// @flow
import Agent from 'src/backend/agent';
import Bridge from 'src/bridge';
import resolveBoxStyle from './resolveBoxStyle';
import type { BackendBridge } from 'src/bridge';
import type { RendererID } from '../types';
import type { StyleAndLayout } from './types';
export type ResolveNativeStyle = (stylesheetID: number) => ?Object;
export default function setupNativeStyleEditor(
bridge: Bridge,
bridge: BackendBridge,
agent: Agent,
resolveNativeStyle: ResolveNativeStyle,
validAttributes?: $ReadOnlyArray<string> | null
@@ -81,7 +81,7 @@ const componentIDToStyleOverrides: Map<number, Object> = new Map();
function measureStyle(
agent: Agent,
bridge: Bridge,
bridge: BackendBridge,
resolveNativeStyle: ResolveNativeStyle,
id: number,
rendererID: RendererID
+4 -4
View File
@@ -2,7 +2,6 @@
import EventEmitter from 'events';
import throttle from 'lodash.throttle';
import Bridge from 'src/bridge';
import {
SESSION_STORAGE_LAST_SELECTION_KEY,
SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
@@ -17,6 +16,7 @@ import {
import setupHighlighter from './views/Highlighter';
import { patch as patchConsole, unpatch as unpatchConsole } from './console';
import type { BackendBridge } from 'src/bridge';
import type {
InstanceAndStyle,
NativeType,
@@ -81,14 +81,14 @@ export default class Agent extends EventEmitter<{|
showNativeHighlight: [NativeType],
shutdown: [],
|}> {
_bridge: Bridge;
_bridge: BackendBridge;
_isProfiling: boolean = false;
_recordChangeDescriptions: boolean = false;
_rendererInterfaces: { [key: RendererID]: RendererInterface } = {};
_persistedSelection: PersistedSelection | null = null;
_persistedSelectionMatch: PathMatch | null = null;
constructor(bridge: Bridge) {
constructor(bridge: BackendBridge) {
super();
if (
@@ -259,7 +259,7 @@ export default class Agent extends EventEmitter<{|
dataURL: string,
rootID: number,
|}) => {
this._bridge.send('screenshotCaptured', { commitIndex, dataURL });
this._bridge.send('screenshotCaptured', { commitIndex, dataURL, rootID });
};
selectElement = ({ id, rendererID }: ElementAndRendererID) => {
+8 -5
View File
@@ -11,6 +11,8 @@ type Rect = {
width: number,
};
type Box = {| top: number, left: number, width: number, height: number |};
// Note that the Overlay components are not affected by the active Theme,
// because they highlight elements in the main Chrome window (outside of devtools).
// The colors below were chosen to roughly match those used by Chrome devtools.
@@ -21,7 +23,7 @@ class OverlayRect {
padding: HTMLElement;
content: HTMLElement;
constructor(doc, container) {
constructor(doc: Document, container: HTMLElement) {
this.node = doc.createElement('div');
this.border = doc.createElement('div');
this.padding = doc.createElement('div');
@@ -51,7 +53,7 @@ class OverlayRect {
}
}
update(box, dims) {
update(box: Rect, dims: any) {
boxWrap(dims, 'margin', this.node);
boxWrap(dims, 'border', this.border);
boxWrap(dims, 'padding', this.padding);
@@ -85,7 +87,7 @@ class OverlayTip {
nameSpan: HTMLElement;
dimSpan: HTMLElement;
constructor(doc, container) {
constructor(doc: Document, container: HTMLElement) {
this.tip = doc.createElement('div');
assign(this.tip.style, {
display: 'flex',
@@ -126,13 +128,13 @@ class OverlayTip {
}
}
updateText(name, width, height) {
updateText(name: string, width: number, height: number) {
this.nameSpan.textContent = name;
this.dimSpan.textContent =
Math.round(width) + 'px × ' + Math.round(height) + 'px';
}
updatePosition(dims, bounds) {
updatePosition(dims: Box, bounds: Box) {
const tipRect = this.tip.getBoundingClientRect();
const tipPos = findTipPos(dims, bounds, {
width: tipRect.width,
@@ -247,6 +249,7 @@ export default class Overlay {
this.tipBoundsWindow.document.documentElement,
this.window
);
this.tip.updatePosition(
{
top: outerBox.top,
+7 -3
View File
@@ -2,11 +2,15 @@
import memoize from 'memoize-one';
import throttle from 'lodash.throttle';
import Bridge from 'src/bridge';
import Agent from 'src/backend/agent';
import { hideOverlay, showOverlay } from './Highlighter';
export default function setup(bridge: Bridge, agent: Agent): void {
import type { BackendBridge } from 'src/bridge';
export default function setupHighlighter(
bridge: BackendBridge,
agent: Agent
): void {
bridge.addListener(
'clearNativeElementHighlight',
clearNativeElementHighlight
@@ -50,7 +54,7 @@ export default function setup(bridge: Bridge, agent: Agent): void {
rendererID,
scrollIntoView,
}: {
displayName: string,
displayName: string | null,
hideAfterTimeout: boolean,
id: number,
openNativeElementsPanel: boolean,
+48 -27
View File
@@ -22,7 +22,7 @@ type Message = {|
type HighlightElementInDOM = {|
...ElementAndRendererID,
displayName: string,
displayName: string | null,
hideAfterTimeout: boolean,
openNativeElementsPanel: boolean,
scrollIntoView: boolean,
@@ -62,33 +62,48 @@ type NativeStyleEditor_SetValueParams = {|
value: string,
|};
export default class Bridge extends EventEmitter<{|
type BackendEvents = {|
captureScreenshot: [{| commitIndex: number, rootID: number |}],
inspectedElement: [InspectedElementPayload],
isBackendStorageAPISupported: [boolean],
operations: [Array<number>],
ownersList: [OwnersList],
overrideComponentFilters: [Array<ComponentFilter>],
profilingData: [ProfilingDataBackend],
profilingStatus: [boolean],
reloadAppForProfiling: [],
screenshotCaptured: [
{| commitIndex: number, dataURL: string, rootID: number |},
],
selectFiber: [number],
shutdown: [],
stopInspectingNative: [boolean],
syncSelectionFromNativeElementsPanel: [],
syncSelectionToNativeElementsPanel: [],
// React Native style editor plug-in.
isNativeStyleEditorSupported: [
{| isSupported: boolean, validAttributes: ?$ReadOnlyArray<string> |},
],
NativeStyleEditor_styleAndLayout: [StyleAndLayoutPayload],
|};
type FrontendEvents = {|
captureScreenshot: [{| commitIndex: number, rootID: number |}],
clearNativeElementHighlight: [],
getOwnersList: [ElementAndRendererID],
getProfilingData: [{| rendererID: RendererID |}],
getProfilingStatus: [],
highlightNativeElement: [HighlightElementInDOM],
init: [],
inspectElement: [InspectElementParams],
inspectedElement: [InspectedElementPayload],
isBackendStorageAPISupported: [boolean],
logElementToConsole: [ElementAndRendererID],
operations: [Array<number>],
ownersList: [OwnersList],
overrideComponentFilters: [Array<ComponentFilter>],
overrideContext: [OverrideValue],
overrideHookState: [OverrideHookState],
overrideProps: [OverrideValue],
overrideState: [OverrideValue],
overrideSuspense: [OverrideSuspense],
profilingData: [ProfilingDataBackend],
profilingStatus: [boolean],
reloadAndProfile: [boolean],
reloadAppForProfiling: [],
screenshotCaptured: [
{| commitIndex: number, dataURL: string, rootID: number |},
],
selectElement: [ElementAndRendererID],
selectFiber: [number],
shutdown: [],
@@ -96,20 +111,22 @@ export default class Bridge extends EventEmitter<{|
startProfiling: [boolean],
stopInspectingNative: [boolean],
stopProfiling: [],
syncSelectionFromNativeElementsPanel: [],
syncSelectionToNativeElementsPanel: [],
updateAppendComponentStack: [boolean],
updateComponentFilters: [Array<ComponentFilter>],
viewElementSource: [ElementAndRendererID],
// React Native style editor plug-in.
isNativeStyleEditorSupported: [
{| isSupported: boolean, validAttributes: $ReadOnlyArray<string> |},
],
NativeStyleEditor_measure: [ElementAndRendererID],
NativeStyleEditor_renameAttribute: [NativeStyleEditor_RenameAttributeParams],
NativeStyleEditor_setValue: [NativeStyleEditor_SetValueParams],
NativeStyleEditor_styleAndLayout: [StyleAndLayoutPayload],
|};
class Bridge<
OutgoingEvents: Object,
IncomingEvents: Object
> extends EventEmitter<{|
...IncomingEvents,
...OutgoingEvents,
|}> {
_isShutdown: boolean = false;
_messageQueue: Array<any> = [];
@@ -134,7 +151,10 @@ export default class Bridge extends EventEmitter<{|
return this._wall;
}
send(event: string, payload: any, transferable?: Array<any>) {
send<EventName: $Keys<OutgoingEvents>>(
event: EventName,
...payload: $ElementType<OutgoingEvents, EventName>
) {
if (this._isShutdown) {
console.warn(
`Cannot send message "${event}" through a Bridge that has been shutdown.`
@@ -150,7 +170,7 @@ export default class Bridge extends EventEmitter<{|
// - if there *has* been a message flushed in the last BATCH_DURATION ms
// (or we're waiting for our setTimeout-0 to fire), then _timeoutID will
// be set, and we'll simply add to the queue and wait for that
this._messageQueue.push(event, payload, transferable);
this._messageQueue.push(event, payload);
if (!this._timeoutID) {
this._timeoutID = setTimeout(this._flush, 0);
}
@@ -204,12 +224,8 @@ export default class Bridge extends EventEmitter<{|
this._timeoutID = null;
if (this._messageQueue.length) {
for (let i = 0; i < this._messageQueue.length; i += 3) {
this._wall.send(
this._messageQueue[i],
this._messageQueue[i + 1],
this._messageQueue[i + 2]
);
for (let i = 0; i < this._messageQueue.length; i += 2) {
this._wall.send(this._messageQueue[i], ...this._messageQueue[i + 1]);
}
this._messageQueue.length = 0;
@@ -220,3 +236,8 @@ export default class Bridge extends EventEmitter<{|
}
};
}
export type BackendBridge = Bridge<BackendEvents, FrontendEvents>;
export type FrontendBridge = Bridge<FrontendEvents, BackendEvents>;
export default Bridge;
+7 -3
View File
@@ -3,11 +3,11 @@
import EventEmitter from 'events';
import memoize from 'memoize-one';
import throttle from 'lodash.throttle';
import Bridge from 'src/bridge';
import { prepareProfilingDataFrontendFromBackendAndStore } from './views/Profiler/utils';
import ProfilingCache from './ProfilingCache';
import Store from './store';
import type { FrontendBridge } from 'src/bridge';
import type { ProfilingDataBackend } from 'src/backend/types';
import type {
CommitDataFrontend,
@@ -23,7 +23,7 @@ export default class ProfilerStore extends EventEmitter<{|
isProfiling: [],
profilingData: [],
|}> {
_bridge: Bridge;
_bridge: FrontendBridge;
// Suspense cache for lazily calculating derived profiling data.
_cache: ProfilingCache;
@@ -79,7 +79,11 @@ export default class ProfilerStore extends EventEmitter<{|
_store: Store;
constructor(bridge: Bridge, store: Store, defaultIsProfiling: boolean) {
constructor(
bridge: FrontendBridge,
store: Store,
defaultIsProfiling: boolean
) {
super();
this._bridge = bridge;
+2 -2
View File
@@ -1,6 +1,6 @@
// @flow
import Bridge from 'src/bridge';
import type { FrontendBridge } from 'src/bridge';
type Shell = {|
connect: (callback: Function) => void,
@@ -8,7 +8,7 @@ type Shell = {|
|};
export function initDevTools(shell: Shell) {
shell.connect((bridge: Bridge) => {
shell.connect((bridge: FrontendBridge) => {
// TODO ...
});
}
+4 -4
View File
@@ -2,7 +2,6 @@
import EventEmitter from 'events';
import { inspect } from 'util';
import Bridge from 'src/bridge';
import {
TREE_OPERATION_ADD,
TREE_OPERATION_REMOVE,
@@ -24,6 +23,7 @@ import ProfilerStore from './ProfilerStore';
import type { Element } from './views/Components/types';
import type { ComponentFilter, ElementType } from '../types';
import type { FrontendBridge } from 'src/bridge';
const debug = (methodName, ...args) => {
if (__DEBUG__) {
@@ -71,7 +71,7 @@ export default class Store extends EventEmitter<{|
supportsProfiling: [],
supportsReloadAndProfile: [],
|}> {
_bridge: Bridge;
_bridge: FrontendBridge;
_captureScreenshots: boolean = false;
@@ -129,7 +129,7 @@ export default class Store extends EventEmitter<{|
// Used for windowing purposes.
_weightAcrossRoots: number = 0;
constructor(bridge: Bridge, config?: Config) {
constructor(bridge: FrontendBridge, config?: Config) {
super();
if (__DEBUG__) {
@@ -701,7 +701,7 @@ export default class Store extends EventEmitter<{|
validAttributes,
}: {|
isSupported: boolean,
validAttributes: $ReadOnlyArray<string>,
validAttributes: ?$ReadOnlyArray<string>,
|}) => {
this._isNativeStyleEditorSupported = isSupported;
this._nativeStyleEditorValidAttributes = validAttributes || null;
+13 -11
View File
@@ -221,17 +221,19 @@ function HookView({
if (canEditHooks && isStateEditable) {
overrideValueFn = (absolutePath: Array<string | number>, value: any) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideHookState', {
id,
hookID,
// Hooks override function expects a relative path for the specified hook (id),
// starting with its id within the (flat) hooks list structure.
// This relative path does not include the fake tree structure DevTools uses for display,
// so it's important that we remove that part of the path before sending the update.
path: absolutePath.slice(path.length + 1),
rendererID,
value,
});
if (rendererID !== null) {
bridge.send('overrideHookState', {
id,
hookID,
// Hooks override function expects a relative path for the specified hook (id),
// starting with its id within the (flat) hooks list structure.
// This relative path does not include the fake tree structure DevTools uses for display,
// so it's important that we remove that part of the path before sending the update.
path: absolutePath.slice(path.length + 1),
rendererID,
value,
});
}
};
}
@@ -85,7 +85,9 @@ function InspectedElementContextController({ children }: Props) {
const getInspectedElementPath = useCallback<GetInspectedElementPath>(
(id: number, path: Array<string | number>) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('inspectElement', { id, path, rendererID });
if (rendererID !== null) {
bridge.send('inspectElement', { id, path, rendererID });
}
},
[bridge, store]
);
@@ -232,7 +234,9 @@ function InspectedElementContextController({ children }: Props) {
const sendRequest = () => {
timeoutID = null;
bridge.send('inspectElement', { id: selectedElementID, rendererID });
if (rendererID !== null) {
bridge.send('inspectElement', { id: selectedElementID, rendererID });
}
};
// Send the initial inspection request.
@@ -240,7 +244,9 @@ function InspectedElementContextController({ children }: Props) {
sendRequest();
// Update the $r variable.
bridge.send('selectElement', { id: selectedElementID, rendererID });
if (rendererID !== null) {
bridge.send('selectElement', { id: selectedElementID, rendererID });
}
const onInspectedElement = (data: InspectedElementPayload) => {
// If this is the element we requested, wait a little bit and then ask for another update.
@@ -25,22 +25,28 @@ export default function StyleEditor({ id, style }: Props) {
const store = useContext(StoreContext);
const changeAttribute = (oldName: string, newName: string, value: any) => {
bridge.send('NativeStyleEditor_renameAttribute', {
id,
rendererID: store.getRendererIDForElement(id),
oldName,
newName,
value,
});
const rendererID = store.getRendererIDForElement(id);
if (rendererID !== null) {
bridge.send('NativeStyleEditor_renameAttribute', {
id,
rendererID,
oldName,
newName,
value,
});
}
};
const changeValue = (name: string, value: any) => {
bridge.send('NativeStyleEditor_setValue', {
id,
rendererID: store.getRendererIDForElement(id),
name,
value,
});
const rendererID = store.getRendererIDForElement(id);
if (rendererID !== null) {
bridge.send('NativeStyleEditor_setValue', {
id,
rendererID,
name,
value,
});
}
};
const keys = useMemo(() => Array.from(Object.keys(style)), [style]);
@@ -136,10 +136,12 @@ function NativeStyleContextController({ children }: Props) {
const sendRequest = () => {
timeoutID = null;
bridge.send('NativeStyleEditor_measure', {
id: selectedElementID,
rendererID,
});
if (rendererID !== null) {
bridge.send('NativeStyleEditor_measure', {
id: selectedElementID,
rendererID,
});
}
};
// Send the initial measurement request.
@@ -106,8 +106,9 @@ function OwnersListContextController({ children }: Props) {
useEffect(() => {
if (ownerID !== null) {
const rendererID = store.getRendererIDForElement(ownerID);
bridge.send('getOwnersList', { id: ownerID, rendererID });
if (rendererID !== null) {
bridge.send('getOwnersList', { id: ownerID, rendererID });
}
}
return () => {};
@@ -140,12 +140,18 @@ export default function SelectedElement(_: Props) {
});
}
const rendererID = store.getRendererIDForElement(
nearestSuspenseElementID
);
// Toggle suspended
bridge.send('overrideSuspense', {
id: nearestSuspenseElementID,
rendererID: store.getRendererIDForElement(nearestSuspenseElementID),
forceFallback: !isSuspended,
});
if (rendererID !== null) {
bridge.send('overrideSuspense', {
id: nearestSuspenseElementID,
rendererID,
forceFallback: !isSuspended,
});
}
}
}, [bridge, dispatch, element, isSuspended, modalDialogDispatch, store]);
@@ -280,15 +286,21 @@ function InspectedElementView({
if (type === ElementTypeClass) {
overrideContextFn = (path: Array<string | number>, value: any) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideContext', { id, path, rendererID, value });
if (rendererID !== null) {
bridge.send('overrideContext', { id, path, rendererID, value });
}
};
overridePropsFn = (path: Array<string | number>, value: any) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideProps', { id, path, rendererID, value });
if (rendererID !== null) {
bridge.send('overrideProps', { id, path, rendererID, value });
}
};
overrideStateFn = (path: Array<string | number>, value: any) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideState', { id, path, rendererID, value });
if (rendererID !== null) {
bridge.send('overrideState', { id, path, rendererID, value });
}
};
} else if (
(type === ElementTypeFunction ||
@@ -298,7 +310,9 @@ function InspectedElementView({
) {
overridePropsFn = (path: Array<string | number>, value: any) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideProps', { id, path, rendererID, value });
if (rendererID !== null) {
bridge.send('overrideProps', { id, path, rendererID, value });
}
};
} else if (type === ElementTypeSuspense && canToggleSuspense) {
overrideSuspenseFn = (path: Array<string | number>, value: boolean) => {
@@ -306,7 +320,13 @@ function InspectedElementView({
throw new Error('Unexpected path.');
}
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideSuspense', { id, rendererID, forceFallback: value });
if (rendererID !== null) {
bridge.send('overrideSuspense', {
id,
rendererID,
forceFallback: value,
});
}
};
}
+1 -1
View File
@@ -192,7 +192,7 @@ export default function Tree(props: Props) {
(id: number) => {
const element = store.getElementByID(id);
const rendererID = store.getRendererIDForElement(id);
if (element !== null) {
if (element !== null && rendererID !== null) {
bridge.send('highlightNativeElement', {
displayName: element.displayName,
hideAfterTimeout: false,
+2 -2
View File
@@ -6,7 +6,6 @@ import '@reach/menu-button/styles.css';
import '@reach/tooltip/styles.css';
import React, { useMemo, useState } from 'react';
import Bridge from 'src/bridge';
import Store from '../store';
import { BridgeContext, StoreContext } from './context';
import Components from './Components/Components';
@@ -25,6 +24,7 @@ import styles from './DevTools.css';
import './root.css';
import type { InspectedElement } from 'src/devtools/views/Components/types';
import type { FrontendBridge } from 'src/bridge';
export type BrowserTheme = 'dark' | 'light';
export type TabID = 'components' | 'profiler' | 'settings';
@@ -34,7 +34,7 @@ export type ViewElementSource = (
) => void;
export type Props = {|
bridge: Bridge,
bridge: FrontendBridge,
browserTheme?: BrowserTheme,
defaultTab?: TabID,
showTabBar?: boolean,
+5 -3
View File
@@ -1,11 +1,13 @@
// @flow
import { createContext } from 'react';
import Bridge from 'src/bridge';
import Store from '../store';
export const BridgeContext = createContext<Bridge>(((null: any): Bridge));
import type { FrontendBridge } from 'src/bridge';
export const BridgeContext = createContext<FrontendBridge>(
((null: any): FrontendBridge)
);
BridgeContext.displayName = 'BridgeContext';
export const StoreContext = createContext<Store>(((null: any): Store));