@samitouri / QOS-React / commits / 18dff7990a

[DevTools] add support for HostSingleton & HostResource (#25616)

## Summary This is to support two new reconciler work tags `HostSingleton` and `HostResource` introduced in PRs #25243 #25426. The behavior is described below. I also renamed an option in components settings from an internal concept "host" to more understood "dom nodes" ## How did you test this change? Tested on the latest Vercel playground app https://github.com/vercel/app-playground/ Before the change, devtools cannot show correct display name for these new elements. Also, some unnecessary internal details are exposed to users. <img width="1395" alt="image" src="https://user-images.githubusercontent.com/1001890/199578181-c4e4ea74-baa1-4507-83d0-91a62ad7de5f.png"> After the change, the display names are correctly shown and the "state" would always be hidden in the detail view. <img width="1417" alt="image" src="https://user-images.githubusercontent.com/1001890/199578442-adc1951d-7d5b-4b84-ad64-85bcf7a8ebcc.png"> These elements will also be hidden just like other native dom elements (e.g. `<div>`) <img width="836" alt="image" src="https://user-images.githubusercontent.com/1001890/199578598-2dfacf64-ddc9-42b5-a246-dd0b09f629af.png">

Mengdi Chen committed Nov 7, 2022 at 15:23 UTC 18dff7990a81b3ff1e716dd2cf113c6027bdcc06
4 files changed +64 -40
packages/react-devtools-shared/src/backend/renderer.js
+18
@@ -237,6 +237,8 @@ export function getInternalReactConstants(
237 HostComponent: 5,
238 HostPortal: 4,
239 HostRoot: 3,
240 + HostResource: 26, // In reality, 18.2+. But doesn't hurt to include it here
241 + HostSingleton: 27, // Same as above
242 HostText: 6,
243 IncompleteClassComponent: 17,
244 IndeterminateComponent: 2,
@@ -269,6 +271,8 @@ export function getInternalReactConstants(
271 HostComponent: 5,
272 HostPortal: 4,
273 HostRoot: 3,
274 + HostResource: -1, // Doesn't exist yet
275 + HostSingleton: -1, // Doesn't exist yet
276 HostText: 6,
277 IncompleteClassComponent: 17,
278 IndeterminateComponent: 2,
@@ -300,6 +304,8 @@ export function getInternalReactConstants(
304 HostComponent: 5,
305 HostPortal: 4,
306 HostRoot: 3,
307 + HostResource: -1, // Doesn't exist yet
308 + HostSingleton: -1, // Doesn't exist yet
309 HostText: 6,
310 IncompleteClassComponent: 17,
311 IndeterminateComponent: 2,
@@ -331,6 +337,8 @@ export function getInternalReactConstants(
337 HostComponent: 7,
338 HostPortal: 6,
339 HostRoot: 5,
340 + HostResource: -1, // Doesn't exist yet
341 + HostSingleton: -1, // Doesn't exist yet
342 HostText: 8,
343 IncompleteClassComponent: -1, // Doesn't exist yet
344 IndeterminateComponent: 4,
@@ -362,6 +370,8 @@ export function getInternalReactConstants(
370 HostComponent: 5,
371 HostPortal: 4,
372 HostRoot: 3,
373 + HostResource: -1, // Doesn't exist yet
374 + HostSingleton: -1, // Doesn't exist yet
375 HostText: 6,
376 IncompleteClassComponent: -1, // Doesn't exist yet
377 IndeterminateComponent: 0,
@@ -401,6 +411,8 @@ export function getInternalReactConstants(
411 IndeterminateComponent,
412 ForwardRef,
413 HostRoot,
414 + HostResource,
415 + HostSingleton,
416 HostComponent,
417 HostPortal,
418 HostText,
@@ -466,6 +478,8 @@ export function getInternalReactConstants(
478 }
479 return null;
480 case HostComponent:
481 + case HostSingleton:
482 + case HostResource:
483 return type;
484 case HostPortal:
485 case HostText:
@@ -600,6 +614,8 @@ export function attach(
614 Fragment,
615 FunctionComponent,
616 HostRoot,
617 + HostResource,
618 + HostSingleton,
619 HostPortal,
620 HostComponent,
621 HostText,
@@ -1044,6 +1060,8 @@ export function attach(
1060 case HostRoot:
1061 return ElementTypeRoot;
1062 case HostComponent:
1063 + case HostResource:
1064 + case HostSingleton:
1065 return ElementTypeHostComponent;
1066 case HostPortal:
1067 case HostText:
packages/react-devtools-shared/src/backend/types.js
+2
@@ -40,6 +40,8 @@ export type WorkTagMap = {
40 HostComponent: WorkTag,
41 HostPortal: WorkTag,
42 HostRoot: WorkTag,
43 + HostResource: WorkTag,
44 + HostSingleton: WorkTag,
45 HostText: WorkTag,
46 IncompleteClassComponent: WorkTag,
47 IndeterminateComponent: WorkTag,
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementStateTree.js
+43 -39
@@ -9,6 +9,7 @@
9
10 import {copy} from 'clipboard-js';
11 import * as React from 'react';
12 +import {ElementTypeHostComponent} from 'react-devtools-shared/src/types';
13 import Button from '../Button';
14 import ButtonIcon from '../ButtonIcon';
15 import KeyValue from './KeyValue';
@@ -33,52 +34,55 @@ export default function InspectedElementStateTree({
34 inspectedElement,
35 store,
36 }: Props): React.Node {
36 - const {state} = inspectedElement;
37 + const {state, type} = inspectedElement;
38 +
39 + // HostSingleton and HostResource may have state that we don't want to expose to users
40 + const isHostComponent = type === ElementTypeHostComponent;
41
42 const entries = state != null ? Object.entries(state) : null;
43 + const isEmpty = entries === null || entries.length === 0;
44 +
45 + if (isEmpty || isHostComponent) {
46 + return null;
47 + }
48 +
49 if (entries !== null) {
50 entries.sort(alphaSortEntries);
51 }
52
43 - const isEmpty = entries === null || entries.length === 0;
44 -
53 const handleCopy = () => copy(serializeDataForCopy(((state: any): Object)));
54
47 - if (isEmpty) {
48 - return null;
49 - } else {
50 - return (
51 - <div className={styles.InspectedElementTree}>
52 - <div className={styles.HeaderRow}>
53 - <div className={styles.Header}>state</div>
54 - {!isEmpty && (
55 - <Button onClick={handleCopy} title="Copy to clipboard">
56 - <ButtonIcon type="copy" />
57 - </Button>
58 - )}
59 - </div>
60 - {isEmpty && <div className={styles.Empty}>None</div>}
61 - {!isEmpty &&
62 - (entries: any).map(([name, value]) => (
63 - <KeyValue
64 - key={name}
65 - alphaSort={true}
66 - bridge={bridge}
67 - canDeletePaths={true}
68 - canEditValues={true}
69 - canRenamePaths={true}
70 - depth={1}
71 - element={element}
72 - hidden={false}
73 - inspectedElement={inspectedElement}
74 - name={name}
75 - path={[name]}
76 - pathRoot="state"
77 - store={store}
78 - value={value}
79 - />
80 - ))}
55 + return (
56 + <div className={styles.InspectedElementTree}>
57 + <div className={styles.HeaderRow}>
58 + <div className={styles.Header}>state</div>
59 + {!isEmpty && (
60 + <Button onClick={handleCopy} title="Copy to clipboard">
61 + <ButtonIcon type="copy" />
62 + </Button>
63 + )}
64 </div>
82 - );
83 - }
65 + {isEmpty && <div className={styles.Empty}>None</div>}
66 + {!isEmpty &&
67 + (entries: any).map(([name, value]) => (
68 + <KeyValue
69 + key={name}
70 + alphaSort={true}
71 + bridge={bridge}
72 + canDeletePaths={true}
73 + canEditValues={true}
74 + canRenamePaths={true}
75 + depth={1}
76 + element={element}
77 + hidden={false}
78 + inspectedElement={inspectedElement}
79 + name={name}
80 + path={[name]}
81 + pathRoot="state"
82 + store={store}
83 + value={value}
84 + />
85 + ))}
86 + </div>
87 + );
88 }
packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js
+1 -1
@@ -373,7 +373,7 @@ export default function ComponentsSettings(_: {}): React.Node {
373 <option value={ElementTypeFunction}>function</option>
374 <option value={ElementTypeForwardRef}>forward ref</option>
375 <option value={ElementTypeHostComponent}>
376 - host (e.g. &lt;div&gt;)
376 + dom nodes (e.g. &lt;div&gt;)
377 </option>
378 <option value={ElementTypeMemo}>memo</option>
379 <option value={ElementTypeOtherOrUnknown}>other</option>