Add a separate button for logging values
Dan Abramov committed
Apr 8, 2019 at 18:37 UTC
5b120d6b24e29972ec8bc8b00638051c55f08a43
6 files changed
+81
-4
src/backend/agent.js
+10
@@ -75,6 +75,7 @@ export default class Agent extends EventEmitter {
75
bridge.addListener('getProfilingSummary', this.getProfilingSummary);
76
bridge.addListener('highlightElementInDOM', this.highlightElementInDOM);
77
bridge.addListener('inspectElement', this.inspectElement);
78
+ bridge.addListener('logElementToConsole', this.logElementToConsole);
79
bridge.addListener('overrideContext', this.overrideContext);
80
bridge.addListener('overrideHookState', this.overrideHookState);
81
bridge.addListener('overrideProps', this.overrideProps);
@@ -268,6 +269,15 @@ export default class Agent extends EventEmitter {
269
}
270
};
271
272
+ logElementToConsole = ({ id, rendererID }: InspectSelectParams) => {
273
+ const renderer = this._rendererInterfaces[rendererID];
274
+ if (renderer == null) {
275
+ console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
276
+ } else {
277
+ renderer.logElementToConsole(id);
278
+ }
279
+ };
280
+
281
reloadAndProfile = () => {
282
localStorage.setItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
283
src/backend/renderer.js
+40
@@ -1530,6 +1530,8 @@ export function attach(
1530
// Can view component source location.
1531
canViewSource,
1532
1533
+ displayName: getDataForFiber(fiber).displayName,
1534
+
1535
// Inspectable properties.
1536
// TODO Review sanitization approach for the below inspectable values.
1537
context,
@@ -1560,6 +1562,43 @@ export function attach(
1562
return result;
1563
}
1564
1565
+ function logElementToConsole(id) {
1566
+ const result = inspectElementRaw(id);
1567
+ if (result === null) {
1568
+ console.warn(`Could not find Fiber with id "${id}"`);
1569
+ return;
1570
+ }
1571
+
1572
+ const supportsGroup = typeof console.groupCollapsed === 'function';
1573
+ const label =
1574
+ '[Click to expand] <' + (result.displayName || 'Component') + ' />';
1575
+
1576
+ if (supportsGroup) {
1577
+ console.groupCollapsed(label);
1578
+ }
1579
+ if (result.props !== null) {
1580
+ console.log('Props:', result.props);
1581
+ }
1582
+ if (result.state !== null) {
1583
+ console.log('State:', result.state);
1584
+ }
1585
+ if (result.hooks !== null) {
1586
+ console.log('Hooks:', result.hooks);
1587
+ }
1588
+ const nativeNode = findNativeByFiberID(id);
1589
+ if (nativeNode !== null) {
1590
+ console.log('Node:', nativeNode);
1591
+ }
1592
+ if (window.chrome || /firefox/i.test(navigator.userAgent)) {
1593
+ console.log(
1594
+ 'Right-click any value to save it as a global variable for further inspection.'
1595
+ );
1596
+ }
1597
+ if (supportsGroup) {
1598
+ console.groupEnd();
1599
+ }
1600
+ }
1601
+
1602
function setInHook(
1603
id: number,
1604
index: number,
@@ -1870,6 +1909,7 @@ export function attach(
1909
handleCommitFiberRoot,
1910
handleCommitFiberUnmount,
1911
inspectElement,
1912
+ logElementToConsole,
1913
prepareViewElementSource,
1914
overrideSuspense,
1915
renderer,
src/backend/types.js
+1
@@ -106,6 +106,7 @@ export type RendererInterface = {
106
handleCommitFiberRoot: (fiber: Object) => void,
107
handleCommitFiberUnmount: (fiber: Object) => void,
108
inspectElement: (id: number) => InspectedElement | null,
109
+ logElementToConsole: (id: number) => void,
110
overrideSuspense: (id: number, forceFallback: boolean) => void,
111
prepareViewElementSource: (id: number) => void,
112
renderer: ReactRenderer | null,
src/devtools/views/ButtonIcon.js
+4
@@ -12,6 +12,7 @@ export type IconType =
12
| 'export'
13
| 'filter'
14
| 'import'
15
+ | 'log-data'
16
| 'more'
17
| 'next'
18
| 'previous'
@@ -54,6 +55,9 @@ export default function ButtonIcon({ type }: Props) {
55
case 'import':
56
pathData = PATH_IMPORT;
57
break;
58
+ case 'log-data':
59
+ pathData = PATH_EXPORT; // TODO: real icon
60
+ break;
61
case 'more':
62
pathData = PATH_MORE;
63
break;
src/devtools/views/Components/SelectedElement.js
+24
-4
@@ -39,14 +39,14 @@ export default function SelectedElement(_: Props) {
39
const inspectedElement = useInspectedElement(selectedElementID);
40
41
const highlightElement = useCallback(() => {
42
- if (element !== null && selectedElementID !== null) {
43
- const rendererID =
44
- store.getRendererIDForElement(selectedElementID) || null;
42
+ const id = selectedElementID;
43
+ if (element !== null && id !== null) {
44
+ const rendererID = store.getRendererIDForElement(id);
45
if (rendererID !== null) {
46
bridge.send('highlightElementInDOM', {
47
displayName: element.displayName,
48
hideAfterTimeout: true,
49
- id: selectedElementID,
49
+ id,
50
rendererID,
51
scrollIntoView: true,
52
});
@@ -54,6 +54,19 @@ export default function SelectedElement(_: Props) {
54
}
55
}, [bridge, element, selectedElementID, store]);
56
57
+ const logElement = useCallback(() => {
58
+ const id = selectedElementID;
59
+ if (id !== null) {
60
+ const rendererID = store.getRendererIDForElement(id);
61
+ if (rendererID !== null) {
62
+ bridge.send('logElementToConsole', {
63
+ id,
64
+ rendererID,
65
+ });
66
+ }
67
+ }
68
+ }, [bridge, selectedElementID, store]);
69
+
70
const viewSource = useCallback(() => {
71
if (viewElementSource != null && selectedElementID !== null) {
72
viewElementSource(selectedElementID);
@@ -89,6 +102,13 @@ export default function SelectedElement(_: Props) {
102
>
103
<ButtonIcon type="view-dom" />
104
</Button>
105
+ <Button
106
+ className={styles.IconButton}
107
+ onClick={logElement}
108
+ title="Log this component data to the console"
109
+ >
110
+ <ButtonIcon type="log-data" />
111
+ </Button>
112
<Button
113
className={styles.IconButton}
114
disabled={!canViewSource}
src/devtools/views/Components/types.js
+2
@@ -58,6 +58,8 @@ export type InspectedElement = {|
58
59
// Location of component in source coude.
60
source: Object | null,
61
+
62
+ displayName: string | null,
63
|};
64
65
// TODO: Add profiling type