Fixed hydration for events meta data
Brian Vaughn committed
Jun 18, 2019 at 10:50 UTC
a2c79f2d9e4ec0a631859e226ce91a824892e62c
4 files changed
+96
-29
src/backend/renderer.js
+41
-22
@@ -2149,25 +2149,37 @@ export function attach(
2149
});
2150
}
2151
2152
- function createIsPathWhitelisted(isHooksPath: boolean, key: string | null) {
2152
+ function createIsPathWhitelisted(
2153
+ key: string | null,
2154
+ secondaryCategory: 'events' | 'hooks' | null
2155
+ ) {
2156
// This function helps prevent previously-inspected paths from being dehydrated in updates.
2157
// This is important to avoid a bad user experience where expanded toggles collapse on update.
2158
return function isPathWhitelisted(path: Array<string | number>): boolean {
2156
- // Dehydrating the 'subHooks' property makes the HooksTree UI a lot more complicated,
2157
- // so it's easiest for now if we just don't break on this boundary.
2158
- // We can always dehydrate a level deeper (in the value object).
2159
- if (isHooksPath) {
2160
- if (path.length === 1) {
2161
- // Never dehydrate the hooks object at the top level.
2162
- return true;
2163
- }
2164
- if (
2165
- path[path.length - 1] === 'subHooks' ||
2166
- path[path.length - 2] === 'subHooks'
2167
- ) {
2168
- // Never dehydrate the subHooks array
2169
- return true;
2170
- }
2159
+ switch (secondaryCategory) {
2160
+ case 'events':
2161
+ if (path.length <= 2) {
2162
+ // Never dehydrate the "hooks" object at the top level (becaues it's always just an array).
2163
+ return true;
2164
+ }
2165
+ break;
2166
+ case 'hooks':
2167
+ if (path.length === 1) {
2168
+ // Never dehydrate the "hooks" object at the top levels.
2169
+ return true;
2170
+ }
2171
+ if (
2172
+ path[path.length - 1] === 'subHooks' ||
2173
+ path[path.length - 2] === 'subHooks'
2174
+ ) {
2175
+ // Dehydrating the 'subHooks' property makes the HooksTree UI a lot more complicated,
2176
+ // so it's easiest for now if we just don't break on this boundary.
2177
+ // We can always dehydrate a level deeper (in the value object).
2178
+ return true;
2179
+ }
2180
+ break;
2181
+ default:
2182
+ break;
2183
}
2184
2185
let current =
@@ -2195,6 +2207,13 @@ export function attach(
2207
if (path != null) {
2208
mergeInspectedPaths(path);
2209
2210
+ let secondaryCategory = null;
2211
+ if (path[0] === 'events') {
2212
+ secondaryCategory = 'events';
2213
+ } else if (path[0] === 'hooks') {
2214
+ secondaryCategory = 'hooks';
2215
+ }
2216
+
2217
// If this element has not been updated since it was last inspected,
2218
// we can just return the subset of data in the newly-inspected path.
2219
return {
@@ -2206,7 +2225,7 @@ export function attach(
2225
((mostRecentlyInspectedElement: any): InspectedElement),
2226
path
2227
),
2209
- createIsPathWhitelisted(path[0] === 'hooks', null),
2228
+ createIsPathWhitelisted(null, secondaryCategory),
2229
path
2230
),
2231
};
@@ -2246,23 +2265,23 @@ export function attach(
2265
const cleanedInspectedElement = { ...mostRecentlyInspectedElement };
2266
cleanedInspectedElement.context = cleanForBridge(
2267
cleanedInspectedElement.context,
2249
- createIsPathWhitelisted(false, 'context')
2268
+ createIsPathWhitelisted('context', null)
2269
);
2270
cleanedInspectedElement.events = cleanForBridge(
2271
cleanedInspectedElement.events,
2253
- createIsPathWhitelisted(false, 'events')
2272
+ createIsPathWhitelisted('events', 'events')
2273
);
2274
cleanedInspectedElement.hooks = cleanForBridge(
2275
cleanedInspectedElement.hooks,
2257
- createIsPathWhitelisted(true, 'hooks')
2276
+ createIsPathWhitelisted('hooks', 'hooks')
2277
);
2278
cleanedInspectedElement.props = cleanForBridge(
2279
cleanedInspectedElement.props,
2261
- createIsPathWhitelisted(false, 'props')
2280
+ createIsPathWhitelisted('props', null)
2281
);
2282
cleanedInspectedElement.state = cleanForBridge(
2283
cleanedInspectedElement.state,
2265
- createIsPathWhitelisted(false, 'state')
2284
+ createIsPathWhitelisted('state', null)
2285
);
2286
2287
return {
src/devtools/views/Components/EventsTree.css
+4
@@ -20,6 +20,10 @@
20
display: flex;
21
}
22
23
+.Name {
24
+ user-select: none;
25
+}
26
+
27
.Empty {
28
color: var(--color-dimmer);
29
font-style: italic;
src/devtools/views/Components/EventsTree.js
+44
-6
@@ -1,3 +1,5 @@
1
+// @flow
2
+
3
import { copy } from 'clipboard-js';
4
import React, { useCallback, useState } from 'react';
5
import styles from './EventsTree.css';
@@ -7,15 +9,32 @@ import KeyValue from './KeyValue';
9
import ExpandCollapseToggle from './ExpandCollapseToggle';
10
import { serializeDataForCopy } from '../utils';
11
10
-type Props = {|
12
+import type { GetInspectedElementPath } from './InspectedElementContext';
13
+
14
+type InspectPath = (path: Array<string | number>) => void;
15
+
16
+type EventsTreeViewProps = {|
17
events: Object,
18
+ getInspectedElementPath: GetInspectedElementPath,
19
+ id: number,
20
|};
21
14
-function EventsTreeView({ events }: Props) {
22
+function EventsTreeView({
23
+ events,
24
+ getInspectedElementPath,
25
+ id,
26
+}: EventsTreeViewProps) {
27
const handleCopy = useCallback(() => copy(serializeDataForCopy(events)), [
28
events,
29
]);
30
31
+ const inspectPath = useCallback(
32
+ (path: Array<string | number>) => {
33
+ getInspectedElementPath(id, ['events', ...path]);
34
+ },
35
+ [getInspectedElementPath, id]
36
+ );
37
+
38
return (
39
<div className={styles.EventsTree}>
40
<div className={styles.HeaderRow}>
@@ -26,16 +45,26 @@ function EventsTreeView({ events }: Props) {
45
</Button>
46
}
47
</div>
29
- <InnerEventsTreeView events={events} />
48
+ <InnerEventsTreeView events={events} inspectPath={inspectPath} />
49
</div>
50
);
51
}
52
34
-function InnerEventsTreeView({ events }: Props) {
53
+type InnerEventsTreeViewProps = {|
54
+ events: Object,
55
+ inspectPath: InspectPath,
56
+|};
57
+
58
+function InnerEventsTreeView({
59
+ events,
60
+ inspectPath,
61
+}: InnerEventsTreeViewProps) {
62
return events.map((event, index) => (
63
<EventComponentView
64
key={index}
65
displayName={event.displayName}
66
+ index={index}
67
+ inspectPath={inspectPath}
68
props={event.props}
69
/>
70
));
@@ -43,11 +72,19 @@ function InnerEventsTreeView({ events }: Props) {
72
73
type EventComponentViewProps = {|
74
displayName: string,
75
+ index: number,
76
+ inspectPath: InspectPath,
77
props: null | Object,
78
|};
79
49
-function EventComponentView({ displayName, props }: EventComponentViewProps) {
80
+function EventComponentView({
81
+ displayName,
82
+ index,
83
+ inspectPath,
84
+ props,
85
+}: EventComponentViewProps) {
86
const [isOpen, setIsOpen] = useState(false);
87
+
88
let eventComponentProps = null;
89
// eslint-disable-next-line no-unused-vars
90
let children;
@@ -74,8 +111,9 @@ function EventComponentView({ displayName, props }: EventComponentViewProps) {
111
<KeyValue
112
key={name}
113
depth={1}
114
+ inspectPath={inspectPath}
115
name={name}
78
- path={[name]}
116
+ path={[index]}
117
value={(eventComponentProps: any)[name]}
118
/>
119
))}
src/devtools/views/Components/SelectedElement.js
+7
-1
@@ -336,7 +336,13 @@ function InspectedElementView({
336
inspectPath={inspectContextPath}
337
overrideValueFn={overrideContextFn}
338
/>
339
- {events !== null && events.length > 0 && <EventsTree events={events} />}
339
+ {events !== null && events.length > 0 && (
340
+ <EventsTree
341
+ events={events}
342
+ getInspectedElementPath={getInspectedElementPath}
343
+ id={id}
344
+ />
345
+ )}
346
347
{ownerID === null && owners !== null && owners.length > 0 && (
348
<div className={styles.Owners}>