Skip to content

Commit db8542a

Browse files
author
Brian Vaughn
authored
Refactor inspect/select logic so that $r contains hooks data (#364)
* Refactor inspect/select logic so that var contains hooks data * Legacy renderer resets $r to null when inspecting non class/function element
1 parent 644c9c4 commit db8542a

File tree

7 files changed

+105
-110
lines changed

7 files changed

+105
-110
lines changed

packages/react-devtools-core/src/backend.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ export function connectToDevTools(options: ?ConnectOptions) {
112112
},
113113
});
114114
bridge.addListener(
115-
'selectElement',
116-
({ id, rendererID }: {| id: number, rendererID: number |}) => {
115+
'inspectElement',
116+
({ id, rendererID }: { id: number, rendererID: number }) => {
117117
const renderer = agent.rendererInterfaces[rendererID];
118118
if (renderer != null) {
119119
// Send event for RN to highlight.

src/backend/agent.js

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ export default class Agent extends EventEmitter<{|
124124
bridge.addListener('overrideState', this.overrideState);
125125
bridge.addListener('overrideSuspense', this.overrideSuspense);
126126
bridge.addListener('reloadAndProfile', this.reloadAndProfile);
127-
bridge.addListener('selectElement', this.selectElement);
128127
bridge.addListener('startProfiling', this.startProfiling);
129128
bridge.addListener('stopProfiling', this.stopProfiling);
130129
bridge.addListener(
@@ -219,6 +218,24 @@ export default class Agent extends EventEmitter<{|
219218
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
220219
} else {
221220
this._bridge.send('inspectedElement', renderer.inspectElement(id, path));
221+
222+
// When user selects an element, stop trying to restore the selection,
223+
// and instead remember the current selection for the next reload.
224+
if (
225+
this._persistedSelectionMatch === null ||
226+
this._persistedSelectionMatch.id !== id
227+
) {
228+
this._persistedSelection = null;
229+
this._persistedSelectionMatch = null;
230+
renderer.setTrackedPath(null);
231+
this._throttledPersistSelection(rendererID, id);
232+
}
233+
234+
// TODO: If there was a way to change the selected DOM element
235+
// in native Elements tab without forcing a switch to it, we'd do it here.
236+
// For now, it doesn't seem like there is a way to do that:
237+
// https://github.com/bvaughn/react-devtools-experimental/issues/102
238+
// (Setting $0 doesn't work, and calling inspect() switches the tab.)
222239
}
223240
};
224241

@@ -244,33 +261,6 @@ export default class Agent extends EventEmitter<{|
244261
this._bridge.send('reloadAppForProfiling');
245262
};
246263

247-
selectElement = ({ id, rendererID }: ElementAndRendererID) => {
248-
const renderer = this._rendererInterfaces[rendererID];
249-
if (renderer == null) {
250-
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
251-
} else {
252-
renderer.selectElement(id);
253-
254-
// When user selects an element, stop trying to restore the selection,
255-
// and instead remember the current selection for the next reload.
256-
if (
257-
this._persistedSelectionMatch === null ||
258-
this._persistedSelectionMatch.id !== id
259-
) {
260-
this._persistedSelection = null;
261-
this._persistedSelectionMatch = null;
262-
renderer.setTrackedPath(null);
263-
this._throttledPersistSelection(rendererID, id);
264-
}
265-
266-
// TODO: If there was a way to change the selected DOM element
267-
// in native Elements tab without forcing a switch to it, we'd do it here.
268-
// For now, it doesn't seem like there is a way to do that:
269-
// https://github.com/bvaughn/react-devtools-experimental/issues/102
270-
// (Setting $0 doesn't work, and calling inspect() switches the tab.)
271-
}
272-
};
273-
274264
overrideContext = ({ id, path, rendererID, value }: SetInParams) => {
275265
const renderer = this._rendererInterfaces[rendererID];
276266
if (renderer == null) {

src/backend/legacy/renderer.js

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,35 @@ export function attach(
609609
};
610610
}
611611

612+
function updateSelectedElement(id: number): void {
613+
const internalInstance = idToInternalInstanceMap.get(id);
614+
if (internalInstance == null) {
615+
console.warn(`Could not find instance with id "${id}"`);
616+
return;
617+
}
618+
619+
switch (getElementType(internalInstance)) {
620+
case ElementTypeClass:
621+
global.$r = internalInstance._instance;
622+
break;
623+
case ElementTypeFunction:
624+
const element = internalInstance._currentElement;
625+
if (element == null) {
626+
console.warn(`Could not find element with id "${id}"`);
627+
return;
628+
}
629+
630+
global.$r = {
631+
props: element.props,
632+
type: element.type,
633+
};
634+
break;
635+
default:
636+
global.$r = null;
637+
break;
638+
}
639+
}
640+
612641
function inspectElement(
613642
id: number,
614643
path?: Array<string | number>
@@ -630,6 +659,11 @@ export function attach(
630659
mergeInspectedPaths(path);
631660
}
632661

662+
// Any time an inspected element has an update,
663+
// we should update the selected $r value as wel.
664+
// Do this before dehyration (cleanForBridge).
665+
updateSelectedElement(id);
666+
633667
inspectedElement.context = cleanForBridge(
634668
inspectedElement.context,
635669
createIsPathWhitelisted('context')
@@ -777,34 +811,6 @@ export function attach(
777811
global.$type = element.type;
778812
}
779813

780-
function selectElement(id: number): void {
781-
const internalInstance = idToInternalInstanceMap.get(id);
782-
if (internalInstance == null) {
783-
console.warn(`Could not find instance with id "${id}"`);
784-
return;
785-
}
786-
787-
switch (getElementType(internalInstance)) {
788-
case ElementTypeClass:
789-
global.$r = internalInstance._instance;
790-
break;
791-
case ElementTypeFunction:
792-
const element = internalInstance._currentElement;
793-
if (element == null) {
794-
console.warn(`Could not find element with id "${id}"`);
795-
return;
796-
}
797-
798-
global.$r = {
799-
props: element.props,
800-
type: element.type,
801-
};
802-
break;
803-
default:
804-
break;
805-
}
806-
}
807-
808814
function setInProps(id: number, path: Array<string | number>, value: any) {
809815
const internalInstance = idToInternalInstanceMap.get(id);
810816
if (internalInstance != null) {
@@ -918,7 +924,6 @@ export function attach(
918924
overrideSuspense,
919925
prepareViewElementSource,
920926
renderer,
921-
selectElement,
922927
setInContext,
923928
setInHook,
924929
setInProps,

src/backend/renderer.js

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,49 +1988,6 @@ export function attach(
19881988
}
19891989
// END copied code
19901990

1991-
function selectElement(id: number): void {
1992-
let fiber = idToFiberMap.get(id);
1993-
if (fiber == null) {
1994-
console.warn(`Could not find Fiber with id "${id}"`);
1995-
return;
1996-
}
1997-
1998-
const { elementType, memoizedProps, stateNode, tag, type } = fiber;
1999-
2000-
switch (tag) {
2001-
case ClassComponent:
2002-
case IncompleteClassComponent:
2003-
case IndeterminateComponent:
2004-
global.$r = stateNode;
2005-
break;
2006-
case FunctionComponent:
2007-
global.$r = {
2008-
props: memoizedProps,
2009-
type,
2010-
};
2011-
break;
2012-
case ForwardRef:
2013-
global.$r = {
2014-
props: memoizedProps,
2015-
type: type.render,
2016-
};
2017-
break;
2018-
case MemoComponent:
2019-
case SimpleMemoComponent:
2020-
global.$r = {
2021-
props: memoizedProps,
2022-
type:
2023-
elementType != null && elementType.type != null
2024-
? elementType.type
2025-
: type,
2026-
};
2027-
break;
2028-
default:
2029-
global.$r = null;
2030-
break;
2031-
}
2032-
}
2033-
20341991
function prepareViewElementSource(id: number): void {
20351992
let fiber = idToFiberMap.get(id);
20361993
if (fiber == null) {
@@ -2341,6 +2298,52 @@ export function attach(
23412298
};
23422299
}
23432300

2301+
function updateSelectedElement(inspectedElement: InspectedElement): void {
2302+
const { hooks, id, props } = inspectedElement;
2303+
2304+
let fiber = idToFiberMap.get(id);
2305+
if (fiber == null) {
2306+
console.warn(`Could not find Fiber with id "${id}"`);
2307+
return;
2308+
}
2309+
2310+
const { elementType, stateNode, tag, type } = fiber;
2311+
2312+
switch (tag) {
2313+
case ClassComponent:
2314+
case IncompleteClassComponent:
2315+
case IndeterminateComponent:
2316+
global.$r = stateNode;
2317+
break;
2318+
case FunctionComponent:
2319+
global.$r = {
2320+
hooks,
2321+
props,
2322+
type,
2323+
};
2324+
break;
2325+
case ForwardRef:
2326+
global.$r = {
2327+
props,
2328+
type: type.render,
2329+
};
2330+
break;
2331+
case MemoComponent:
2332+
case SimpleMemoComponent:
2333+
global.$r = {
2334+
props,
2335+
type:
2336+
elementType != null && elementType.type != null
2337+
? elementType.type
2338+
: type,
2339+
};
2340+
break;
2341+
default:
2342+
global.$r = null;
2343+
break;
2344+
}
2345+
}
2346+
23442347
function inspectElement(
23452348
id: number,
23462349
path?: Array<string | number>
@@ -2401,6 +2404,11 @@ export function attach(
24012404
mergeInspectedPaths(path);
24022405
}
24032406

2407+
// Any time an inspected element has an update,
2408+
// we should update the selected $r value as wel.
2409+
// Do this before dehyration (cleanForBridge).
2410+
updateSelectedElement(mostRecentlyInspectedElement);
2411+
24042412
// Clone before cleaning so that we preserve the full data.
24052413
// This will enable us to send patches without re-inspecting if hydrated paths are requested.
24062414
// (Reducing how often we shallow-render is a better DX for function components that use hooks.)
@@ -2986,7 +2994,6 @@ export function attach(
29862994
prepareViewElementSource,
29872995
overrideSuspense,
29882996
renderer,
2989-
selectElement,
29902997
setInContext,
29912998
setInHook,
29922999
setInProps,

src/backend/types.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ export type RendererInterface = {
299299
overrideSuspense: (id: number, forceFallback: boolean) => void,
300300
prepareViewElementSource: (id: number) => void,
301301
renderer: ReactRenderer | null,
302-
selectElement: (id: number) => void,
303302
setInContext: (id: number, path: Array<string | number>, value: any) => void,
304303
setInHook: (
305304
id: number,

src/bridge.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ type FrontendEvents = {|
9999
overrideSuspense: [OverrideSuspense],
100100
profilingData: [ProfilingDataBackend],
101101
reloadAndProfile: [boolean],
102-
selectElement: [ElementAndRendererID],
103102
selectFiber: [number],
104103
shutdown: [],
105104
startInspectingNative: [],

src/devtools/views/Components/InspectedElementContext.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,6 @@ function InspectedElementContextController({ children }: Props) {
241241
// We'll poll for an update in the response handler below.
242242
sendRequest();
243243

244-
// Update the $r variable.
245-
if (rendererID !== null) {
246-
bridge.send('selectElement', { id: selectedElementID, rendererID });
247-
}
248-
249244
const onInspectedElement = (data: InspectedElementPayload) => {
250245
// If this is the element we requested, wait a little bit and then ask for another update.
251246
if (data.id === selectedElementID) {

0 commit comments

Comments
 (0)