@samitouri / QOS-React-2 / commits / db8542ad97

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

Brian Vaughn committed Aug 3, 2019 at 17:59 UTC db8542ad97c75c9f61a949209f6c054a1c0c9688
7 files changed +105 -110
packages/react-devtools-core/src/backend.js
+2 -2
@@ -112,8 +112,8 @@ export function connectToDevTools(options: ?ConnectOptions) {
112 },
113 });
114 bridge.addListener(
115 - 'selectElement',
116 - ({ id, rendererID }: {| id: number, rendererID: number |}) => {
115 + 'inspectElement',
116 + ({ id, rendererID }: { id: number, rendererID: number }) => {
117 const renderer = agent.rendererInterfaces[rendererID];
118 if (renderer != null) {
119 // Send event for RN to highlight.
src/backend/agent.js
+18 -28
@@ -124,7 +124,6 @@ export default class Agent extends EventEmitter<{|
124 bridge.addListener('overrideState', this.overrideState);
125 bridge.addListener('overrideSuspense', this.overrideSuspense);
126 bridge.addListener('reloadAndProfile', this.reloadAndProfile);
127 - bridge.addListener('selectElement', this.selectElement);
127 bridge.addListener('startProfiling', this.startProfiling);
128 bridge.addListener('stopProfiling', this.stopProfiling);
129 bridge.addListener(
@@ -219,6 +218,24 @@ export default class Agent extends EventEmitter<{|
218 console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
219 } else {
220 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.)
239 }
240 };
241
@@ -244,33 +261,6 @@ export default class Agent extends EventEmitter<{|
261 this._bridge.send('reloadAppForProfiling');
262 };
263
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 -
264 overrideContext = ({ id, path, rendererID, value }: SetInParams) => {
265 const renderer = this._rendererInterfaces[rendererID];
266 if (renderer == null) {
src/backend/legacy/renderer.js
+34 -29
@@ -609,6 +609,35 @@ export function attach(
609 };
610 }
611
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 +
641 function inspectElement(
642 id: number,
643 path?: Array<string | number>
@@ -630,6 +659,11 @@ export function attach(
659 mergeInspectedPaths(path);
660 }
661
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 +
667 inspectedElement.context = cleanForBridge(
668 inspectedElement.context,
669 createIsPathWhitelisted('context')
@@ -777,34 +811,6 @@ export function attach(
811 global.$type = element.type;
812 }
813
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 -
814 function setInProps(id: number, path: Array<string | number>, value: any) {
815 const internalInstance = idToInternalInstanceMap.get(id);
816 if (internalInstance != null) {
@@ -918,7 +924,6 @@ export function attach(
924 overrideSuspense,
925 prepareViewElementSource,
926 renderer,
921 - selectElement,
927 setInContext,
928 setInHook,
929 setInProps,
src/backend/renderer.js
+51 -44
@@ -1988,49 +1988,6 @@ export function attach(
1988 }
1989 // END copied code
1990
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 -
1991 function prepareViewElementSource(id: number): void {
1992 let fiber = idToFiberMap.get(id);
1993 if (fiber == null) {
@@ -2341,6 +2298,52 @@ export function attach(
2298 };
2299 }
2300
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 +
2347 function inspectElement(
2348 id: number,
2349 path?: Array<string | number>
@@ -2401,6 +2404,11 @@ export function attach(
2404 mergeInspectedPaths(path);
2405 }
2406
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 +
2412 // Clone before cleaning so that we preserve the full data.
2413 // This will enable us to send patches without re-inspecting if hydrated paths are requested.
2414 // (Reducing how often we shallow-render is a better DX for function components that use hooks.)
@@ -2986,7 +2994,6 @@ export function attach(
2994 prepareViewElementSource,
2995 overrideSuspense,
2996 renderer,
2989 - selectElement,
2997 setInContext,
2998 setInHook,
2999 setInProps,
src/backend/types.js
-1
@@ -299,7 +299,6 @@ export type RendererInterface = {
299 overrideSuspense: (id: number, forceFallback: boolean) => void,
300 prepareViewElementSource: (id: number) => void,
301 renderer: ReactRenderer | null,
302 - selectElement: (id: number) => void,
302 setInContext: (id: number, path: Array<string | number>, value: any) => void,
303 setInHook: (
304 id: number,
src/bridge.js
-1
@@ -99,7 +99,6 @@ type FrontendEvents = {|
99 overrideSuspense: [OverrideSuspense],
100 profilingData: [ProfilingDataBackend],
101 reloadAndProfile: [boolean],
102 - selectElement: [ElementAndRendererID],
102 selectFiber: [number],
103 shutdown: [],
104 startInspectingNative: [],
src/devtools/views/Components/InspectedElementContext.js
-5
@@ -241,11 +241,6 @@ function InspectedElementContextController({ children }: Props) {
241 // We'll poll for an update in the response handler below.
242 sendRequest();
243
244 - // Update the $r variable.
245 - if (rendererID !== null) {
246 - bridge.send('selectElement', { id: selectedElementID, rendererID });
247 - }
248 -
244 const onInspectedElement = (data: InspectedElementPayload) => {
245 // If this is the element we requested, wait a little bit and then ask for another update.
246 if (data.id === selectedElementID) {