// @flow import { copy } from 'clipboard-js'; import React, { useCallback, useContext, useState } from 'react'; import { BridgeContext, StoreContext } from '../context'; import Button from '../Button'; import ButtonIcon from '../ButtonIcon'; import EditableValue from './EditableValue'; import ExpandCollapseToggle from './ExpandCollapseToggle'; import { InspectedElementContext } from './InspectedElementContext'; import KeyValue from './KeyValue'; import { serializeHooksForCopy } from '../utils'; import styles from './HooksTree.css'; import { meta } from '../../../hydration'; import type { InspectPath } from './SelectedElement'; import type { HooksNode, HooksTree } from 'src/backend/types'; type HooksTreeViewProps = {| canEditHooks: boolean, hooks: HooksTree | null, id: number, |}; export function HooksTreeView({ canEditHooks, hooks, id }: HooksTreeViewProps) { const { getInspectedElementPath } = useContext(InspectedElementContext); const inspectPath = useCallback( (path: Array) => { getInspectedElementPath(id, ['hooks', ...path]); }, [getInspectedElementPath, id] ); const handleCopy = useCallback(() => copy(serializeHooksForCopy(hooks)), [ hooks, ]); if (hooks === null) { return null; } else { return (
hooks
); } } type InnerHooksTreeViewProps = {| canEditHooks: boolean, hooks: HooksTree, id: number, inspectPath: InspectPath, path: Array, |}; export function InnerHooksTreeView({ canEditHooks, hooks, id, inspectPath, path, }: InnerHooksTreeViewProps) { // $FlowFixMe "Missing type annotation for U" whatever that means return hooks.map((hook, index) => ( )); } type HookViewProps = {| canEditHooks: boolean, hook: HooksNode, id: number, inspectPath: InspectPath, path: Array, |}; function HookView({ canEditHooks, hook, id, inspectPath, path, }: HookViewProps) { const { name, id: hookID, isStateEditable, subHooks, value } = hook; const bridge = useContext(BridgeContext); const store = useContext(StoreContext); const [isOpen, setIsOpen] = useState(false); const toggleIsOpen = useCallback( () => setIsOpen(prevIsOpen => !prevIsOpen), [] ); if (hook.hasOwnProperty(meta.inspected)) { // This Hook is too deep and hasn't been hydrated. if (__DEV__) { console.warn('Unexpected dehydrated hook; this is a DevTools error.'); } return (
...
); } const isCustomHook = subHooks.length > 0; const type = typeof value; let displayValue; let isComplexDisplayValue = false; // Format data for display to mimic the props/state/context for now. if (type === 'string') { displayValue = `"${((value: any): string)}"`; } else if (type === 'boolean') { displayValue = value ? 'true' : 'false'; } else if (type === 'number') { displayValue = value; } else if (value === null) { displayValue = 'null'; } else if (value === undefined) { displayValue = null; } else if (Array.isArray(value)) { isComplexDisplayValue = true; displayValue = 'Array'; } else if (type === 'object') { isComplexDisplayValue = true; displayValue = 'Object'; } if (isCustomHook) { const subHooksView = Array.isArray(subHooks) ? ( ) : ( ); if (isComplexDisplayValue) { return (
{name || 'Anonymous'}
); } else { return (
{name || 'Anonymous'} {' '} {/* $FlowFixMe */} {displayValue}
); } } else { let overrideValueFn = null; // TODO Maybe read editable value from debug hook? if (canEditHooks && isStateEditable) { overrideValueFn = (absolutePath: Array, value: any) => { const rendererID = store.getRendererIDForElement(id); bridge.send('overrideHookState', { id, hookID, // Hooks override function expects a relative path for the specified hook (id), // starting with its id within the (flat) hooks list structure. // This relative path does not include the fake tree structure DevTools uses for display, // so it's important that we remove that part of the path before sending the update. path: absolutePath.slice(path.length + 1), rendererID, value, }); }; } if (isComplexDisplayValue) { return (
); } else { return (
{name} {typeof overrideValueFn === 'function' ? ( ) : ( // $FlowFixMe Cannot create span element because in property children {displayValue} )}
); } } } // $FlowFixMe export default React.memo(HooksTreeView);