Fixed hydration for events meta data

This commit is contained in:
Brian Vaughn 2019-06-18 10:50:19 -07:00
parent cf19916be7
commit a2c79f2d9e
4 changed files with 96 additions and 29 deletions

View File

@ -2149,25 +2149,37 @@ export function attach(
});
}
function createIsPathWhitelisted(isHooksPath: boolean, key: string | null) {
function createIsPathWhitelisted(
key: string | null,
secondaryCategory: 'events' | 'hooks' | null
) {
// This function helps prevent previously-inspected paths from being dehydrated in updates.
// This is important to avoid a bad user experience where expanded toggles collapse on update.
return function isPathWhitelisted(path: Array<string | number>): boolean {
// Dehydrating the 'subHooks' property makes the HooksTree UI a lot more complicated,
// so it's easiest for now if we just don't break on this boundary.
// We can always dehydrate a level deeper (in the value object).
if (isHooksPath) {
if (path.length === 1) {
// Never dehydrate the hooks object at the top level.
return true;
}
if (
path[path.length - 1] === 'subHooks' ||
path[path.length - 2] === 'subHooks'
) {
// Never dehydrate the subHooks array
return true;
}
switch (secondaryCategory) {
case 'events':
if (path.length <= 2) {
// Never dehydrate the "hooks" object at the top level (becaues it's always just an array).
return true;
}
break;
case 'hooks':
if (path.length === 1) {
// Never dehydrate the "hooks" object at the top levels.
return true;
}
if (
path[path.length - 1] === 'subHooks' ||
path[path.length - 2] === 'subHooks'
) {
// Dehydrating the 'subHooks' property makes the HooksTree UI a lot more complicated,
// so it's easiest for now if we just don't break on this boundary.
// We can always dehydrate a level deeper (in the value object).
return true;
}
break;
default:
break;
}
let current =
@ -2195,6 +2207,13 @@ export function attach(
if (path != null) {
mergeInspectedPaths(path);
let secondaryCategory = null;
if (path[0] === 'events') {
secondaryCategory = 'events';
} else if (path[0] === 'hooks') {
secondaryCategory = 'hooks';
}
// If this element has not been updated since it was last inspected,
// we can just return the subset of data in the newly-inspected path.
return {
@ -2206,7 +2225,7 @@ export function attach(
((mostRecentlyInspectedElement: any): InspectedElement),
path
),
createIsPathWhitelisted(path[0] === 'hooks', null),
createIsPathWhitelisted(null, secondaryCategory),
path
),
};
@ -2246,23 +2265,23 @@ export function attach(
const cleanedInspectedElement = { ...mostRecentlyInspectedElement };
cleanedInspectedElement.context = cleanForBridge(
cleanedInspectedElement.context,
createIsPathWhitelisted(false, 'context')
createIsPathWhitelisted('context', null)
);
cleanedInspectedElement.events = cleanForBridge(
cleanedInspectedElement.events,
createIsPathWhitelisted(false, 'events')
createIsPathWhitelisted('events', 'events')
);
cleanedInspectedElement.hooks = cleanForBridge(
cleanedInspectedElement.hooks,
createIsPathWhitelisted(true, 'hooks')
createIsPathWhitelisted('hooks', 'hooks')
);
cleanedInspectedElement.props = cleanForBridge(
cleanedInspectedElement.props,
createIsPathWhitelisted(false, 'props')
createIsPathWhitelisted('props', null)
);
cleanedInspectedElement.state = cleanForBridge(
cleanedInspectedElement.state,
createIsPathWhitelisted(false, 'state')
createIsPathWhitelisted('state', null)
);
return {

View File

@ -20,6 +20,10 @@
display: flex;
}
.Name {
user-select: none;
}
.Empty {
color: var(--color-dimmer);
font-style: italic;

View File

@ -1,3 +1,5 @@
// @flow
import { copy } from 'clipboard-js';
import React, { useCallback, useState } from 'react';
import styles from './EventsTree.css';
@ -7,15 +9,32 @@ import KeyValue from './KeyValue';
import ExpandCollapseToggle from './ExpandCollapseToggle';
import { serializeDataForCopy } from '../utils';
type Props = {|
import type { GetInspectedElementPath } from './InspectedElementContext';
type InspectPath = (path: Array<string | number>) => void;
type EventsTreeViewProps = {|
events: Object,
getInspectedElementPath: GetInspectedElementPath,
id: number,
|};
function EventsTreeView({ events }: Props) {
function EventsTreeView({
events,
getInspectedElementPath,
id,
}: EventsTreeViewProps) {
const handleCopy = useCallback(() => copy(serializeDataForCopy(events)), [
events,
]);
const inspectPath = useCallback(
(path: Array<string | number>) => {
getInspectedElementPath(id, ['events', ...path]);
},
[getInspectedElementPath, id]
);
return (
<div className={styles.EventsTree}>
<div className={styles.HeaderRow}>
@ -26,16 +45,26 @@ function EventsTreeView({ events }: Props) {
</Button>
}
</div>
<InnerEventsTreeView events={events} />
<InnerEventsTreeView events={events} inspectPath={inspectPath} />
</div>
);
}
function InnerEventsTreeView({ events }: Props) {
type InnerEventsTreeViewProps = {|
events: Object,
inspectPath: InspectPath,
|};
function InnerEventsTreeView({
events,
inspectPath,
}: InnerEventsTreeViewProps) {
return events.map((event, index) => (
<EventComponentView
key={index}
displayName={event.displayName}
index={index}
inspectPath={inspectPath}
props={event.props}
/>
));
@ -43,11 +72,19 @@ function InnerEventsTreeView({ events }: Props) {
type EventComponentViewProps = {|
displayName: string,
index: number,
inspectPath: InspectPath,
props: null | Object,
|};
function EventComponentView({ displayName, props }: EventComponentViewProps) {
function EventComponentView({
displayName,
index,
inspectPath,
props,
}: EventComponentViewProps) {
const [isOpen, setIsOpen] = useState(false);
let eventComponentProps = null;
// eslint-disable-next-line no-unused-vars
let children;
@ -74,8 +111,9 @@ function EventComponentView({ displayName, props }: EventComponentViewProps) {
<KeyValue
key={name}
depth={1}
inspectPath={inspectPath}
name={name}
path={[name]}
path={[index]}
value={(eventComponentProps: any)[name]}
/>
))}

View File

@ -336,7 +336,13 @@ function InspectedElementView({
inspectPath={inspectContextPath}
overrideValueFn={overrideContextFn}
/>
{events !== null && events.length > 0 && <EventsTree events={events} />}
{events !== null && events.length > 0 && (
<EventsTree
events={events}
getInspectedElementPath={getInspectedElementPath}
id={id}
/>
)}
{ownerID === null && owners !== null && owners.length > 0 && (
<div className={styles.Owners}>