Skip to content

Commit

Permalink
Refactor inspect/select logic so that $r contains hooks data (#364)
Browse files Browse the repository at this point in the history
* Refactor inspect/select logic so that  var contains hooks data
* Legacy renderer resets $r to null when inspecting non class/function element
  • Loading branch information
Brian Vaughn authored Aug 4, 2019
1 parent 644c9c4 commit db8542a
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 110 deletions.
4 changes: 2 additions & 2 deletions packages/react-devtools-core/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ export function connectToDevTools(options: ?ConnectOptions) {
},
});
bridge.addListener(
'selectElement',
({ id, rendererID }: {| id: number, rendererID: number |}) => {
'inspectElement',
({ id, rendererID }: { id: number, rendererID: number }) => {
const renderer = agent.rendererInterfaces[rendererID];
if (renderer != null) {
// Send event for RN to highlight.
Expand Down
46 changes: 18 additions & 28 deletions src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export default class Agent extends EventEmitter<{|
bridge.addListener('overrideState', this.overrideState);
bridge.addListener('overrideSuspense', this.overrideSuspense);
bridge.addListener('reloadAndProfile', this.reloadAndProfile);
bridge.addListener('selectElement', this.selectElement);
bridge.addListener('startProfiling', this.startProfiling);
bridge.addListener('stopProfiling', this.stopProfiling);
bridge.addListener(
Expand Down Expand Up @@ -219,6 +218,24 @@ export default class Agent extends EventEmitter<{|
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
} else {
this._bridge.send('inspectedElement', renderer.inspectElement(id, path));

// When user selects an element, stop trying to restore the selection,
// and instead remember the current selection for the next reload.
if (
this._persistedSelectionMatch === null ||
this._persistedSelectionMatch.id !== id
) {
this._persistedSelection = null;
this._persistedSelectionMatch = null;
renderer.setTrackedPath(null);
this._throttledPersistSelection(rendererID, id);
}

// TODO: If there was a way to change the selected DOM element
// in native Elements tab without forcing a switch to it, we'd do it here.
// For now, it doesn't seem like there is a way to do that:
// https://github.com/bvaughn/react-devtools-experimental/issues/102
// (Setting $0 doesn't work, and calling inspect() switches the tab.)
}
};

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

selectElement = ({ id, rendererID }: ElementAndRendererID) => {
const renderer = this._rendererInterfaces[rendererID];
if (renderer == null) {
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
} else {
renderer.selectElement(id);

// When user selects an element, stop trying to restore the selection,
// and instead remember the current selection for the next reload.
if (
this._persistedSelectionMatch === null ||
this._persistedSelectionMatch.id !== id
) {
this._persistedSelection = null;
this._persistedSelectionMatch = null;
renderer.setTrackedPath(null);
this._throttledPersistSelection(rendererID, id);
}

// TODO: If there was a way to change the selected DOM element
// in native Elements tab without forcing a switch to it, we'd do it here.
// For now, it doesn't seem like there is a way to do that:
// https://github.com/bvaughn/react-devtools-experimental/issues/102
// (Setting $0 doesn't work, and calling inspect() switches the tab.)
}
};

overrideContext = ({ id, path, rendererID, value }: SetInParams) => {
const renderer = this._rendererInterfaces[rendererID];
if (renderer == null) {
Expand Down
63 changes: 34 additions & 29 deletions src/backend/legacy/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,35 @@ export function attach(
};
}

function updateSelectedElement(id: number): void {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance == null) {
console.warn(`Could not find instance with id "${id}"`);
return;
}

switch (getElementType(internalInstance)) {
case ElementTypeClass:
global.$r = internalInstance._instance;
break;
case ElementTypeFunction:
const element = internalInstance._currentElement;
if (element == null) {
console.warn(`Could not find element with id "${id}"`);
return;
}

global.$r = {
props: element.props,
type: element.type,
};
break;
default:
global.$r = null;
break;
}
}

function inspectElement(
id: number,
path?: Array<string | number>
Expand All @@ -630,6 +659,11 @@ export function attach(
mergeInspectedPaths(path);
}

// Any time an inspected element has an update,
// we should update the selected $r value as wel.
// Do this before dehyration (cleanForBridge).
updateSelectedElement(id);

inspectedElement.context = cleanForBridge(
inspectedElement.context,
createIsPathWhitelisted('context')
Expand Down Expand Up @@ -777,34 +811,6 @@ export function attach(
global.$type = element.type;
}

function selectElement(id: number): void {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance == null) {
console.warn(`Could not find instance with id "${id}"`);
return;
}

switch (getElementType(internalInstance)) {
case ElementTypeClass:
global.$r = internalInstance._instance;
break;
case ElementTypeFunction:
const element = internalInstance._currentElement;
if (element == null) {
console.warn(`Could not find element with id "${id}"`);
return;
}

global.$r = {
props: element.props,
type: element.type,
};
break;
default:
break;
}
}

function setInProps(id: number, path: Array<string | number>, value: any) {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance != null) {
Expand Down Expand Up @@ -918,7 +924,6 @@ export function attach(
overrideSuspense,
prepareViewElementSource,
renderer,
selectElement,
setInContext,
setInHook,
setInProps,
Expand Down
95 changes: 51 additions & 44 deletions src/backend/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1988,49 +1988,6 @@ export function attach(
}
// END copied code

function selectElement(id: number): void {
let fiber = idToFiberMap.get(id);
if (fiber == null) {
console.warn(`Could not find Fiber with id "${id}"`);
return;
}

const { elementType, memoizedProps, stateNode, tag, type } = fiber;

switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
case IndeterminateComponent:
global.$r = stateNode;
break;
case FunctionComponent:
global.$r = {
props: memoizedProps,
type,
};
break;
case ForwardRef:
global.$r = {
props: memoizedProps,
type: type.render,
};
break;
case MemoComponent:
case SimpleMemoComponent:
global.$r = {
props: memoizedProps,
type:
elementType != null && elementType.type != null
? elementType.type
: type,
};
break;
default:
global.$r = null;
break;
}
}

function prepareViewElementSource(id: number): void {
let fiber = idToFiberMap.get(id);
if (fiber == null) {
Expand Down Expand Up @@ -2341,6 +2298,52 @@ export function attach(
};
}

function updateSelectedElement(inspectedElement: InspectedElement): void {
const { hooks, id, props } = inspectedElement;

let fiber = idToFiberMap.get(id);
if (fiber == null) {
console.warn(`Could not find Fiber with id "${id}"`);
return;
}

const { elementType, stateNode, tag, type } = fiber;

switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
case IndeterminateComponent:
global.$r = stateNode;
break;
case FunctionComponent:
global.$r = {
hooks,
props,
type,
};
break;
case ForwardRef:
global.$r = {
props,
type: type.render,
};
break;
case MemoComponent:
case SimpleMemoComponent:
global.$r = {
props,
type:
elementType != null && elementType.type != null
? elementType.type
: type,
};
break;
default:
global.$r = null;
break;
}
}

function inspectElement(
id: number,
path?: Array<string | number>
Expand Down Expand Up @@ -2401,6 +2404,11 @@ export function attach(
mergeInspectedPaths(path);
}

// Any time an inspected element has an update,
// we should update the selected $r value as wel.
// Do this before dehyration (cleanForBridge).
updateSelectedElement(mostRecentlyInspectedElement);

// Clone before cleaning so that we preserve the full data.
// This will enable us to send patches without re-inspecting if hydrated paths are requested.
// (Reducing how often we shallow-render is a better DX for function components that use hooks.)
Expand Down Expand Up @@ -2986,7 +2994,6 @@ export function attach(
prepareViewElementSource,
overrideSuspense,
renderer,
selectElement,
setInContext,
setInHook,
setInProps,
Expand Down
1 change: 0 additions & 1 deletion src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ export type RendererInterface = {
overrideSuspense: (id: number, forceFallback: boolean) => void,
prepareViewElementSource: (id: number) => void,
renderer: ReactRenderer | null,
selectElement: (id: number) => void,
setInContext: (id: number, path: Array<string | number>, value: any) => void,
setInHook: (
id: number,
Expand Down
1 change: 0 additions & 1 deletion src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ type FrontendEvents = {|
overrideSuspense: [OverrideSuspense],
profilingData: [ProfilingDataBackend],
reloadAndProfile: [boolean],
selectElement: [ElementAndRendererID],
selectFiber: [number],
shutdown: [],
startInspectingNative: [],
Expand Down
5 changes: 0 additions & 5 deletions src/devtools/views/Components/InspectedElementContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,6 @@ function InspectedElementContextController({ children }: Props) {
// We'll poll for an update in the response handler below.
sendRequest();

// Update the $r variable.
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.
if (data.id === selectedElementID) {
Expand Down

0 comments on commit db8542a

Please sign in to comment.