mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Refactored TreeContext to use less memoization (based on feedback from Sebastian)
This commit is contained in:
@@ -13,7 +13,7 @@ import { ElementTypeClass, ElementTypeFunction } from 'src/devtools/types';
|
||||
import Store from 'src/devtools/store';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
import { createRegExp } from '../utils';
|
||||
import { TreeContext } from './TreeContext';
|
||||
import { TreeDispatcherContext, TreeStateContext } from './TreeContext';
|
||||
import { StoreContext } from '../context';
|
||||
|
||||
import type { ItemData } from './Tree';
|
||||
@@ -28,18 +28,21 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function ElementView({ data, index, style }: Props) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const store = useContext(StoreContext);
|
||||
const {
|
||||
baseDepth,
|
||||
getElementAtIndex,
|
||||
ownerFlatTree,
|
||||
ownerStack,
|
||||
selectOwner,
|
||||
selectedElementID,
|
||||
selectElementByID,
|
||||
} = useContext(TreeContext);
|
||||
const store = useContext(StoreContext);
|
||||
} = useContext(TreeStateContext);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
const element = getElementAtIndex(index);
|
||||
const element =
|
||||
ownerFlatTree !== null
|
||||
? store.getElementByID(ownerFlatTree[index])
|
||||
: store.getElementAtIndex(index);
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const {
|
||||
lastScrolledIDRef,
|
||||
@@ -52,9 +55,9 @@ export default function ElementView({ data, index, style }: Props) {
|
||||
|
||||
const handleDoubleClick = useCallback(() => {
|
||||
if (id !== null) {
|
||||
selectOwner(id);
|
||||
dispatch({ type: 'SELECT_OWNER', payload: id });
|
||||
}
|
||||
}, [id, selectOwner]);
|
||||
}, [dispatch, id]);
|
||||
|
||||
const scrollAnchorStartRef = useRef<HTMLSpanElement | null>(null);
|
||||
const scrollAnchorEndRef = useRef<HTMLSpanElement | null>(null);
|
||||
@@ -102,10 +105,13 @@ export default function ElementView({ data, index, style }: Props) {
|
||||
const handleMouseDown = useCallback(
|
||||
({ metaKey }) => {
|
||||
if (id !== null) {
|
||||
selectElementByID(metaKey ? null : id);
|
||||
dispatch({
|
||||
type: 'SELECT_ELEMENT_BY_ID',
|
||||
payload: metaKey ? null : id,
|
||||
});
|
||||
}
|
||||
},
|
||||
[id, selectElementByID]
|
||||
[dispatch, id]
|
||||
);
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
@@ -234,7 +240,9 @@ type DisplayNameProps = {|
|
||||
|};
|
||||
|
||||
function DisplayName({ displayName, id }: DisplayNameProps) {
|
||||
const { searchIndex, searchResults, searchText } = useContext(TreeContext);
|
||||
const { searchIndex, searchResults, searchText } = useContext(
|
||||
TreeStateContext
|
||||
);
|
||||
const isSearchResult = useMemo(() => {
|
||||
return searchResults.includes(id);
|
||||
}, [id, searchResults]);
|
||||
|
||||
@@ -10,7 +10,7 @@ import React, {
|
||||
import { createResource } from '../../cache';
|
||||
import { BridgeContext, StoreContext } from '../context';
|
||||
import { hydrate } from 'src/hydration';
|
||||
import { TreeContext } from './TreeContext';
|
||||
import { TreeStateContext } from './TreeContext';
|
||||
|
||||
import type {
|
||||
DehydratedData,
|
||||
@@ -38,7 +38,7 @@ type Props = {|
|
||||
function InspectedElementContextController({ children }: Props) {
|
||||
const bridge = useContext(BridgeContext);
|
||||
const store = useContext(StoreContext);
|
||||
const { inspectedElementID } = useContext(TreeContext);
|
||||
const { inspectedElementID } = useContext(TreeStateContext);
|
||||
|
||||
const [count, setCount] = useState<number>(0);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
import Toggle from '../Toggle';
|
||||
import { TreeContext } from './TreeContext';
|
||||
import { TreeDispatcherContext, TreeStateContext } from './TreeContext';
|
||||
import { StoreContext } from '../context';
|
||||
import { useIsOverflowing } from '../hooks';
|
||||
|
||||
@@ -20,9 +20,8 @@ import type { Element } from './types';
|
||||
import styles from './OwnersStack.css';
|
||||
|
||||
export default function OwnerStack() {
|
||||
const { ownerStack, ownerStackIndex, resetOwnerStack } = useContext(
|
||||
TreeContext
|
||||
);
|
||||
const { ownerStack, ownerStackIndex } = useContext(TreeStateContext);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
const [elementsTotalWidth, setElementsTotalWidth] = useState(0);
|
||||
const elementsBarRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -73,7 +72,7 @@ export default function OwnerStack() {
|
||||
<div className={styles.VRule} />
|
||||
<Button
|
||||
className={styles.IconButton}
|
||||
onClick={resetOwnerStack}
|
||||
onClick={() => dispatch({ type: 'RESET_OWNER_STACK' })}
|
||||
title="Back to tree view"
|
||||
>
|
||||
<ButtonIcon type="close" />
|
||||
@@ -91,7 +90,7 @@ function ElementsDropdown({
|
||||
ownerStackIndex,
|
||||
}: ElementsDropdownProps) {
|
||||
const store = useContext(StoreContext);
|
||||
const { selectOwner } = useContext(TreeContext);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
@@ -107,7 +106,7 @@ function ElementsDropdown({
|
||||
<MenuItem
|
||||
key={id}
|
||||
className={styles.Component}
|
||||
onSelect={() => selectOwner(id)}
|
||||
onSelect={() => dispatch({ type: 'SELECT_OWNER', payload: id })}
|
||||
>
|
||||
{((store.getElementByID(id): any): Element).displayName}
|
||||
</MenuItem>
|
||||
@@ -123,7 +122,8 @@ type ElementViewProps = {
|
||||
};
|
||||
function ElementView({ id, index }: ElementViewProps) {
|
||||
const store = useContext(StoreContext);
|
||||
const { ownerStackIndex, selectOwner } = useContext(TreeContext);
|
||||
const { ownerStackIndex } = useContext(TreeStateContext);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
const { displayName } = ((store.getElementByID(id): any): Element);
|
||||
|
||||
@@ -131,9 +131,9 @@ function ElementView({ id, index }: ElementViewProps) {
|
||||
|
||||
const handleChange = useCallback(() => {
|
||||
if (!isChecked) {
|
||||
selectOwner(id);
|
||||
dispatch({ type: 'SELECT_OWNER', payload: id });
|
||||
}
|
||||
}, [id, isChecked, selectOwner]);
|
||||
}, [dispatch, id, isChecked]);
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback, useContext, useEffect, useRef } from 'react';
|
||||
import { TreeContext } from './TreeContext';
|
||||
import { TreeDispatcherContext, TreeStateContext } from './TreeContext';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
import Icon from '../Icon';
|
||||
@@ -11,51 +11,49 @@ import styles from './SearchInput.css';
|
||||
type Props = {||};
|
||||
|
||||
export default function SearchInput(props: Props) {
|
||||
const {
|
||||
goToNextSearchResult,
|
||||
goToPreviousSearchResult,
|
||||
searchIndex,
|
||||
searchResults,
|
||||
searchText,
|
||||
selectNextElementInTree,
|
||||
selectPreviousElementInTree,
|
||||
setSearchText,
|
||||
} = useContext(TreeContext);
|
||||
const { searchIndex, searchResults, searchText } = useContext(
|
||||
TreeStateContext
|
||||
);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const handleTextChange = useCallback(
|
||||
({ currentTarget }) => setSearchText(currentTarget.value),
|
||||
[setSearchText]
|
||||
({ currentTarget }) =>
|
||||
dispatch({ type: 'SET_SEARCH_TEXT', payload: currentTarget.value }),
|
||||
[dispatch]
|
||||
);
|
||||
const resetSearch = useCallback(
|
||||
() => dispatch({ type: 'SET_SEARCH_TEXT', payload: '' }),
|
||||
[dispatch]
|
||||
);
|
||||
const resetSearch = useCallback(() => setSearchText(''), [setSearchText]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
event => {
|
||||
// For convenience, let up/down arrow keys change Tree selection.
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
selectNextElementInTree();
|
||||
dispatch({ type: 'SELECT_NEXT_ELEMENT_IN_TREE' });
|
||||
event.preventDefault();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
selectPreviousElementInTree();
|
||||
dispatch({ type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE' });
|
||||
event.preventDefault();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
[selectNextElementInTree, selectPreviousElementInTree]
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleInputKeyPress = useCallback(
|
||||
({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
goToNextSearchResult();
|
||||
dispatch({ type: 'GO_TO_NEXT_SEARCH_RESULT' });
|
||||
}
|
||||
},
|
||||
[goToNextSearchResult]
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
// Auto-focus search input
|
||||
@@ -106,7 +104,7 @@ export default function SearchInput(props: Props) {
|
||||
<Button
|
||||
className={styles.IconButton}
|
||||
disabled={!searchText}
|
||||
onClick={goToPreviousSearchResult}
|
||||
onClick={() => dispatch({ type: 'GO_TO_PREVIOUS_SEARCH_RESULT' })}
|
||||
title="Scroll to previous search result"
|
||||
>
|
||||
<ButtonIcon type="up" />
|
||||
@@ -114,7 +112,7 @@ export default function SearchInput(props: Props) {
|
||||
<Button
|
||||
className={styles.IconButton}
|
||||
disabled={!searchText}
|
||||
onClick={goToNextSearchResult}
|
||||
onClick={() => dispatch({ type: 'GO_TO_NEXT_SEARCH_RESULT' })}
|
||||
title="Scroll to next search result"
|
||||
>
|
||||
<ButtonIcon type="down" />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import { TreeContext } from './TreeContext';
|
||||
import { TreeDispatcherContext, TreeStateContext } from './TreeContext';
|
||||
import { BridgeContext, StoreContext } from '../context';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
import HooksTree from './HooksTree';
|
||||
import InspectedElementTree from './InspectedElementTree';
|
||||
import { InspectedElementContext } from './InspectedElementContext';
|
||||
import ViewElementSourceContext from './ViewElementSourceContext';
|
||||
import styles from './SelectedElement.css';
|
||||
import {
|
||||
ElementTypeClass,
|
||||
@@ -22,7 +23,8 @@ import type { Element, InspectedElement } from './types';
|
||||
export type Props = {||};
|
||||
|
||||
export default function SelectedElement(_: Props) {
|
||||
const { inspectedElementID, viewElementSource } = useContext(TreeContext);
|
||||
const { inspectedElementID } = useContext(TreeStateContext);
|
||||
const viewElementSource = useContext(ViewElementSourceContext);
|
||||
const bridge = useContext(BridgeContext);
|
||||
const store = useContext(StoreContext);
|
||||
|
||||
@@ -153,7 +155,7 @@ function InspectedElementView({
|
||||
state,
|
||||
} = inspectedElement;
|
||||
|
||||
const { ownerStack } = useContext(TreeContext);
|
||||
const { ownerStack } = useContext(TreeStateContext);
|
||||
const bridge = useContext(BridgeContext);
|
||||
const store = useContext(StoreContext);
|
||||
|
||||
@@ -241,12 +243,16 @@ function InspectedElementView({
|
||||
}
|
||||
|
||||
function OwnerView({ displayName, id }: { displayName: string, id: number }) {
|
||||
const { selectElementByID } = useContext(TreeContext);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
const handleClick = useCallback(() => selectElementByID(id), [
|
||||
id,
|
||||
selectElementByID,
|
||||
]);
|
||||
const handleClick = useCallback(
|
||||
() =>
|
||||
dispatch({
|
||||
type: 'SELECT_ELEMENT_BY_ID',
|
||||
payload: id,
|
||||
}),
|
||||
[dispatch, id]
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -11,7 +11,7 @@ import React, {
|
||||
} from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import { TreeContext } from './TreeContext';
|
||||
import { TreeDispatcherContext, TreeStateContext } from './TreeContext';
|
||||
import { SettingsContext } from '../Settings/SettingsContext';
|
||||
import { BridgeContext, StoreContext } from '../context';
|
||||
import ElementView from './Element';
|
||||
@@ -21,12 +21,9 @@ import SearchInput from './SearchInput';
|
||||
|
||||
import styles from './Tree.css';
|
||||
|
||||
import type { Element } from './types';
|
||||
|
||||
export type ItemData = {|
|
||||
baseDepth: number,
|
||||
numElements: number,
|
||||
getElementAtIndex: (index: number) => Element | null,
|
||||
isNavigatingWithKeyboard: boolean,
|
||||
lastScrolledIDRef: { current: number | null },
|
||||
onElementMouseEnter: (id: number) => void,
|
||||
@@ -36,22 +33,16 @@ export type ItemData = {|
|
||||
type Props = {||};
|
||||
|
||||
export default function Tree(props: Props) {
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
const {
|
||||
baseDepth,
|
||||
getElementAtIndex,
|
||||
numElements,
|
||||
ownerStack,
|
||||
searchIndex,
|
||||
searchResults,
|
||||
selectedElementID,
|
||||
selectedElementIndex,
|
||||
selectChildElementInTree,
|
||||
selectElementAtIndex,
|
||||
selectNextElementInTree,
|
||||
selectOwner,
|
||||
selectParentElementInTree,
|
||||
selectPreviousElementInTree,
|
||||
} = useContext(TreeContext);
|
||||
} = useContext(TreeStateContext);
|
||||
const bridge = useContext(BridgeContext);
|
||||
const store = useContext(StoreContext);
|
||||
const [isNavigatingWithKeyboard, setIsNavigatingWithKeyboard] = useState(
|
||||
@@ -111,7 +102,7 @@ export default function Tree(props: Props) {
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
selectNextElementInTree();
|
||||
dispatch({ type: 'SELECT_NEXT_ELEMENT_IN_TREE' });
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
@@ -123,7 +114,7 @@ export default function Tree(props: Props) {
|
||||
if (element.children.length > 0 && !element.isCollapsed) {
|
||||
store.toggleIsCollapsed(element.id, true);
|
||||
} else {
|
||||
selectParentElementInTree();
|
||||
dispatch({ type: 'SELECT_PARENT_ELEMENT_IN_TREE' });
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -137,13 +128,13 @@ export default function Tree(props: Props) {
|
||||
if (element.children.length > 0 && element.isCollapsed) {
|
||||
store.toggleIsCollapsed(element.id, false);
|
||||
} else {
|
||||
selectChildElementInTree();
|
||||
dispatch({ type: 'SELECT_CHILD_ELEMENT_IN_TREE' });
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
selectPreviousElementInTree();
|
||||
dispatch({ type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE' });
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -160,14 +151,7 @@ export default function Tree(props: Props) {
|
||||
return () => {
|
||||
ownerDocument.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [
|
||||
selectedElementID,
|
||||
selectChildElementInTree,
|
||||
selectNextElementInTree,
|
||||
selectParentElementInTree,
|
||||
selectPreviousElementInTree,
|
||||
store,
|
||||
]);
|
||||
}, [dispatch, selectedElementID, store]);
|
||||
|
||||
// Focus management.
|
||||
const handleBlur = useCallback(() => setTreeFocused(false), []);
|
||||
@@ -175,9 +159,12 @@ export default function Tree(props: Props) {
|
||||
setTreeFocused(true);
|
||||
|
||||
if (selectedElementIndex === null && numElements > 0) {
|
||||
selectElementAtIndex(0);
|
||||
dispatch({
|
||||
type: 'SELECT_ELEMENT_AT_INDEX',
|
||||
payload: 0,
|
||||
});
|
||||
}
|
||||
}, [numElements, selectedElementIndex, selectElementAtIndex]);
|
||||
}, [dispatch, numElements, selectedElementIndex]);
|
||||
|
||||
const handleKeyPress = useCallback(
|
||||
event => {
|
||||
@@ -185,14 +172,14 @@ export default function Tree(props: Props) {
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
if (selectedElementID !== null) {
|
||||
selectOwner(selectedElementID);
|
||||
dispatch({ type: 'SELECT_OWNER', payload: selectedElementID });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
[selectedElementID, selectOwner]
|
||||
[dispatch, selectedElementID]
|
||||
);
|
||||
|
||||
const highlightElementInDOM = useCallback(
|
||||
@@ -270,7 +257,6 @@ export default function Tree(props: Props) {
|
||||
() => ({
|
||||
baseDepth,
|
||||
numElements,
|
||||
getElementAtIndex,
|
||||
isNavigatingWithKeyboard,
|
||||
onElementMouseEnter: handleElementMouseEnter,
|
||||
lastScrolledIDRef,
|
||||
@@ -279,7 +265,6 @@ export default function Tree(props: Props) {
|
||||
[
|
||||
baseDepth,
|
||||
numElements,
|
||||
getElementAtIndex,
|
||||
isNavigatingWithKeyboard,
|
||||
handleElementMouseEnter,
|
||||
lastScrolledIDRef,
|
||||
@@ -328,7 +313,7 @@ export default function Tree(props: Props) {
|
||||
}
|
||||
|
||||
function InnerElementType({ style, ...rest }) {
|
||||
const { ownerStack } = useContext(TreeContext);
|
||||
const { ownerStack } = useContext(TreeStateContext);
|
||||
|
||||
// The list may need to scroll horizontally due to deeply nested elements.
|
||||
// We don't know the maximum scroll width up front, because we're windowing.
|
||||
|
||||
@@ -34,44 +34,98 @@ import Store from '../../store';
|
||||
|
||||
import type { Element } from './types';
|
||||
|
||||
type Context = {|
|
||||
type StateContext = {|
|
||||
// Tree
|
||||
baseDepth: number,
|
||||
numElements: number,
|
||||
selectedElementID: number | null,
|
||||
selectedElementIndex: number | null,
|
||||
getElementAtIndex(index: number): Element | null,
|
||||
selectChildElementInTree(): void,
|
||||
selectElementAtIndex(index: number): void,
|
||||
selectElementByID(id: number | null): void,
|
||||
selectNextElementInTree(): void,
|
||||
selectParentElementInTree(): void,
|
||||
selectPreviousElementInTree(): void,
|
||||
|
||||
// Search
|
||||
searchIndex: number | null,
|
||||
searchResults: Array<number>,
|
||||
searchText: string,
|
||||
setSearchText(text: string): void,
|
||||
goToNextSearchResult(): void,
|
||||
goToPreviousSearchResult(): void,
|
||||
|
||||
// Owners
|
||||
ownerFlatTree: Array<number> | null,
|
||||
ownerStack: Array<number>,
|
||||
ownerStackIndex: number | null,
|
||||
resetOwnerStack(): void,
|
||||
selectOwner(id: number): void,
|
||||
|
||||
// Injected by parent HTML/JavaScript
|
||||
viewElementSource: Function | null,
|
||||
|
||||
// Inspection element panel
|
||||
// Updated separately so we can avoid suspending when selection changes
|
||||
inspectedElementID: number | null,
|
||||
|};
|
||||
|
||||
const TreeContext = createContext<Context>(((null: any): Context));
|
||||
TreeContext.displayName = 'TreeContext';
|
||||
type ACTION_GO_TO_NEXT_SEARCH_RESULT = {|
|
||||
type: 'GO_TO_NEXT_SEARCH_RESULT',
|
||||
|};
|
||||
type ACTION_GO_TO_PREVIOUS_SEARCH_RESULT = {|
|
||||
type: 'GO_TO_PREVIOUS_SEARCH_RESULT',
|
||||
|};
|
||||
type ACTION_HANDLE_STORE_MUTATION = {|
|
||||
type: 'HANDLE_STORE_MUTATION',
|
||||
payload: [Uint32Array, Uint32Array],
|
||||
|};
|
||||
type ACTION_RESET_OWNER_STACK = {|
|
||||
type: 'RESET_OWNER_STACK',
|
||||
|};
|
||||
type ACTION_SELECT_CHILD_ELEMENT_IN_TREE = {|
|
||||
type: 'SELECT_CHILD_ELEMENT_IN_TREE',
|
||||
|};
|
||||
type ACTION_SELECT_ELEMENT_AT_INDEX = {|
|
||||
type: 'SELECT_ELEMENT_AT_INDEX',
|
||||
payload: number | null,
|
||||
|};
|
||||
type ACTION_SELECT_ELEMENT_BY_ID = {|
|
||||
type: 'SELECT_ELEMENT_BY_ID',
|
||||
payload: number | null,
|
||||
|};
|
||||
type ACTION_SELECT_NEXT_ELEMENT_IN_TREE = {|
|
||||
type: 'SELECT_NEXT_ELEMENT_IN_TREE',
|
||||
|};
|
||||
type ACTION_SELECT_PARENT_ELEMENT_IN_TREE = {|
|
||||
type: 'SELECT_PARENT_ELEMENT_IN_TREE',
|
||||
|};
|
||||
type ACTION_SELECT_PREVIOUS_ELEMENT_IN_TREE = {|
|
||||
type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE',
|
||||
|};
|
||||
type ACTION_SELECT_OWNER = {|
|
||||
type: 'SELECT_OWNER',
|
||||
payload: number,
|
||||
|};
|
||||
type ACTION_SET_SEARCH_TEXT = {|
|
||||
type: 'SET_SEARCH_TEXT',
|
||||
payload: string,
|
||||
|};
|
||||
type ACTION_UPDATE_INSPECTED_ELEMENT_ID = {|
|
||||
type: 'UPDATE_INSPECTED_ELEMENT_ID',
|
||||
|};
|
||||
|
||||
type Action =
|
||||
| ACTION_GO_TO_NEXT_SEARCH_RESULT
|
||||
| ACTION_GO_TO_PREVIOUS_SEARCH_RESULT
|
||||
| ACTION_HANDLE_STORE_MUTATION
|
||||
| ACTION_RESET_OWNER_STACK
|
||||
| ACTION_SELECT_CHILD_ELEMENT_IN_TREE
|
||||
| ACTION_SELECT_ELEMENT_AT_INDEX
|
||||
| ACTION_SELECT_ELEMENT_BY_ID
|
||||
| ACTION_SELECT_NEXT_ELEMENT_IN_TREE
|
||||
| ACTION_SELECT_PARENT_ELEMENT_IN_TREE
|
||||
| ACTION_SELECT_PREVIOUS_ELEMENT_IN_TREE
|
||||
| ACTION_SELECT_OWNER
|
||||
| ACTION_SET_SEARCH_TEXT
|
||||
| ACTION_UPDATE_INSPECTED_ELEMENT_ID;
|
||||
|
||||
type DispatcherContext = (action: Action) => void;
|
||||
|
||||
const TreeStateContext = createContext<StateContext>(
|
||||
((null: any): StateContext)
|
||||
);
|
||||
TreeStateContext.displayName = 'TreeStateContext';
|
||||
|
||||
const TreeDispatcherContext = createContext<DispatcherContext>(
|
||||
((null: any): DispatcherContext)
|
||||
);
|
||||
TreeDispatcherContext.displayName = 'TreeDispatcherContext';
|
||||
|
||||
type State = {|
|
||||
// Tree
|
||||
@@ -88,33 +142,13 @@ type State = {|
|
||||
// Owners
|
||||
ownerStack: Array<number>,
|
||||
ownerStackIndex: number | null,
|
||||
_ownerFlatTree: Array<number> | null,
|
||||
ownerFlatTree: Array<number> | null,
|
||||
|
||||
// Inspection element panel
|
||||
inspectedElementID: number | null,
|
||||
|};
|
||||
|
||||
type Action = {|
|
||||
type:
|
||||
| 'GO_TO_NEXT_SEARCH_RESULT'
|
||||
| 'GO_TO_PREVIOUS_SEARCH_RESULT'
|
||||
| 'HANDLE_STORE_MUTATION'
|
||||
| 'RESET_OWNER_STACK'
|
||||
| 'SELECT_CHILD_ELEMENT_IN_TREE'
|
||||
| 'SELECT_ELEMENT_AT_INDEX'
|
||||
| 'SELECT_ELEMENT_BY_ID'
|
||||
| 'SELECT_NEXT_ELEMENT_IN_TREE'
|
||||
| 'SELECT_PARENT_ELEMENT_IN_TREE'
|
||||
| 'SELECT_PREVIOUS_ELEMENT_IN_TREE'
|
||||
| 'SELECT_OWNER'
|
||||
| 'SET_SEARCH_TEXT'
|
||||
| 'UPDATE_INSPECTED_ELEMENT_ID',
|
||||
payload?: any,
|
||||
|};
|
||||
|
||||
function reduceTreeState(store: Store, state: State, action: Action): State {
|
||||
const { type, payload } = action;
|
||||
|
||||
let {
|
||||
numElements,
|
||||
ownerStack,
|
||||
@@ -126,7 +160,7 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
|
||||
|
||||
// Base tree should ignore selected element changes when the owner's tree is active.
|
||||
if (ownerStack.length === 0) {
|
||||
switch (type) {
|
||||
switch (action.type) {
|
||||
case 'HANDLE_STORE_MUTATION':
|
||||
numElements = store.numElements;
|
||||
|
||||
@@ -157,18 +191,18 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
|
||||
}
|
||||
break;
|
||||
case 'SELECT_ELEMENT_AT_INDEX':
|
||||
selectedElementIndex = ((payload: any): number | null);
|
||||
selectedElementIndex = (action: ACTION_SELECT_ELEMENT_AT_INDEX).payload;
|
||||
break;
|
||||
case 'SELECT_ELEMENT_BY_ID':
|
||||
// Skip lookup in this case; it would be redundant.
|
||||
// It might also cause problems if the specified element was inside of a (not yet expanded) subtree.
|
||||
lookupIDForIndex = false;
|
||||
|
||||
selectedElementID = payload;
|
||||
selectedElementID = (action: ACTION_SELECT_ELEMENT_BY_ID).payload;
|
||||
selectedElementIndex =
|
||||
payload === null
|
||||
selectedElementID === null
|
||||
? null
|
||||
: store.getIndexOfElementID(((payload: any): number));
|
||||
: store.getIndexOfElementID(selectedElementID);
|
||||
break;
|
||||
case 'SELECT_NEXT_ELEMENT_IN_TREE':
|
||||
if (
|
||||
@@ -229,8 +263,6 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
|
||||
}
|
||||
|
||||
function reduceSearchState(store: Store, state: State, action: Action): State {
|
||||
const { type, payload } = action;
|
||||
|
||||
let {
|
||||
ownerStack,
|
||||
searchIndex,
|
||||
@@ -252,7 +284,7 @@ function reduceSearchState(store: Store, state: State, action: Action): State {
|
||||
|
||||
// Search isn't supported when the owner's tree is active.
|
||||
if (ownerStack.length === 0) {
|
||||
switch (type) {
|
||||
switch (action.type) {
|
||||
case 'GO_TO_NEXT_SEARCH_RESULT':
|
||||
if (numPrevSearchResults > 0) {
|
||||
didRequestSearch = true;
|
||||
@@ -274,7 +306,7 @@ function reduceSearchState(store: Store, state: State, action: Action): State {
|
||||
const [
|
||||
addedElementIDs,
|
||||
removedElementIDs,
|
||||
] = ((payload: any): Array<Uint32Array>);
|
||||
] = (action: ACTION_HANDLE_STORE_MUTATION).payload;
|
||||
|
||||
removedElementIDs.forEach(id => {
|
||||
// Prune this item from the search results.
|
||||
@@ -336,7 +368,7 @@ function reduceSearchState(store: Store, state: State, action: Action): State {
|
||||
case 'SET_SEARCH_TEXT':
|
||||
searchIndex = null;
|
||||
searchResults = [];
|
||||
searchText = ((payload: any): string);
|
||||
searchText = (action: ACTION_SET_SEARCH_TEXT).payload;
|
||||
|
||||
if (searchText !== '') {
|
||||
const regExp = createRegExp(searchText);
|
||||
@@ -394,24 +426,22 @@ function reduceSearchState(store: Store, state: State, action: Action): State {
|
||||
}
|
||||
|
||||
function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
const { payload, type } = action;
|
||||
|
||||
let {
|
||||
baseDepth,
|
||||
numElements,
|
||||
selectedElementID,
|
||||
selectedElementIndex,
|
||||
ownerFlatTree,
|
||||
ownerStack,
|
||||
ownerStackIndex,
|
||||
searchIndex,
|
||||
searchResults,
|
||||
searchText,
|
||||
_ownerFlatTree,
|
||||
} = state;
|
||||
|
||||
let prevSelectedElementIndex = selectedElementIndex;
|
||||
|
||||
switch (type) {
|
||||
switch (action.type) {
|
||||
case 'HANDLE_STORE_MUTATION':
|
||||
if (ownerStack.length > 0) {
|
||||
let indexOfRemovedItem = -1;
|
||||
@@ -425,15 +455,15 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
if (indexOfRemovedItem >= 0) {
|
||||
ownerStack = ownerStack.slice(0, indexOfRemovedItem);
|
||||
if (ownerStack.length === 0) {
|
||||
_ownerFlatTree = null;
|
||||
ownerFlatTree = null;
|
||||
ownerStackIndex = null;
|
||||
} else {
|
||||
ownerStackIndex = ownerStack.length - 1;
|
||||
}
|
||||
}
|
||||
if (selectedElementID !== null && _ownerFlatTree !== null) {
|
||||
if (selectedElementID !== null && ownerFlatTree !== null) {
|
||||
// Mutation might have caused the index of this ID to shift.
|
||||
selectedElementIndex = _ownerFlatTree.indexOf(selectedElementID);
|
||||
selectedElementIndex = ownerFlatTree.indexOf(selectedElementID);
|
||||
}
|
||||
} else {
|
||||
if (selectedElementID !== null) {
|
||||
@@ -454,30 +484,31 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
selectedElementID !== null
|
||||
? store.getIndexOfElementID(selectedElementID)
|
||||
: null;
|
||||
_ownerFlatTree = null;
|
||||
ownerFlatTree = null;
|
||||
break;
|
||||
case 'SELECT_ELEMENT_AT_INDEX':
|
||||
if (_ownerFlatTree !== null) {
|
||||
selectedElementIndex = ((payload: any): number | null);
|
||||
if (ownerFlatTree !== null) {
|
||||
selectedElementIndex = (action: ACTION_SELECT_ELEMENT_AT_INDEX).payload;
|
||||
}
|
||||
break;
|
||||
case 'SELECT_ELEMENT_BY_ID':
|
||||
if (_ownerFlatTree !== null) {
|
||||
if (ownerFlatTree !== null) {
|
||||
const payload = (action: ACTION_SELECT_ELEMENT_BY_ID).payload;
|
||||
selectedElementIndex =
|
||||
payload === null ? null : _ownerFlatTree.indexOf(payload);
|
||||
payload === null ? null : ownerFlatTree.indexOf(payload);
|
||||
}
|
||||
break;
|
||||
case 'SELECT_NEXT_ELEMENT_IN_TREE':
|
||||
if (_ownerFlatTree !== null && _ownerFlatTree.length > 0) {
|
||||
if (ownerFlatTree !== null && ownerFlatTree.length > 0) {
|
||||
if (selectedElementIndex === null) {
|
||||
selectedElementIndex = 0;
|
||||
} else if (selectedElementIndex + 1 < _ownerFlatTree.length) {
|
||||
} else if (selectedElementIndex + 1 < ownerFlatTree.length) {
|
||||
selectedElementIndex++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'SELECT_PREVIOUS_ELEMENT_IN_TREE':
|
||||
if (_ownerFlatTree !== null && _ownerFlatTree.length > 0) {
|
||||
if (ownerFlatTree !== null && ownerFlatTree.length > 0) {
|
||||
if (selectedElementIndex !== null && selectedElementIndex > 0) {
|
||||
selectedElementIndex--;
|
||||
}
|
||||
@@ -487,7 +518,8 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
// If the Store doesn't have any owners metadata, don't drill into an empty stack.
|
||||
// This is a confusing user experience.
|
||||
if (store.hasOwnerMetadata) {
|
||||
ownerStackIndex = ownerStack.indexOf(payload);
|
||||
const id = (action: ACTION_SELECT_OWNER).payload;
|
||||
ownerStackIndex = ownerStack.indexOf(id);
|
||||
|
||||
// Always force reset selection to be the top of the new owner tree.
|
||||
selectedElementIndex = 0;
|
||||
@@ -498,7 +530,7 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
if (ownerStackIndex < 0) {
|
||||
// Add this new owner, and fill in the owners above it as well.
|
||||
ownerStack = [];
|
||||
let currentOwnerID = ((payload: any): number);
|
||||
let currentOwnerID = id;
|
||||
while (currentOwnerID !== 0) {
|
||||
ownerStack.unshift(currentOwnerID);
|
||||
currentOwnerID = ((store.getElementByID(
|
||||
@@ -524,23 +556,23 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
if (
|
||||
ownerStackIndex !== state.ownerStackIndex ||
|
||||
ownerStack !== state.ownerStack ||
|
||||
type === 'HANDLE_STORE_MUTATION'
|
||||
action.type === 'HANDLE_STORE_MUTATION'
|
||||
) {
|
||||
if (ownerStackIndex === null) {
|
||||
_ownerFlatTree = null;
|
||||
ownerFlatTree = null;
|
||||
baseDepth = 0;
|
||||
numElements = store.numElements;
|
||||
} else {
|
||||
_ownerFlatTree = calculateCurrentOwnerList(
|
||||
ownerFlatTree = calculateCurrentOwnerList(
|
||||
store,
|
||||
ownerStack[ownerStackIndex],
|
||||
ownerStack[ownerStackIndex],
|
||||
[]
|
||||
);
|
||||
|
||||
baseDepth = ((store.getElementByID(_ownerFlatTree[0]): any): Element)
|
||||
baseDepth = ((store.getElementByID(ownerFlatTree[0]): any): Element)
|
||||
.depth;
|
||||
numElements = _ownerFlatTree.length;
|
||||
numElements = ownerFlatTree.length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,8 +580,8 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
if (selectedElementIndex !== prevSelectedElementIndex) {
|
||||
if (selectedElementIndex === null) {
|
||||
selectedElementID = null;
|
||||
} else if (_ownerFlatTree !== null) {
|
||||
selectedElementID = _ownerFlatTree[((selectedElementIndex: any): number)];
|
||||
} else if (ownerFlatTree !== null) {
|
||||
selectedElementID = ownerFlatTree[((selectedElementIndex: any): number)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +599,7 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
|
||||
|
||||
ownerStack,
|
||||
ownerStackIndex,
|
||||
_ownerFlatTree,
|
||||
ownerFlatTree,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -589,13 +621,10 @@ function reduceSuspenseState(
|
||||
}
|
||||
}
|
||||
|
||||
type Props = {|
|
||||
children: React$Node,
|
||||
viewElementSource: Function | null,
|
||||
|};
|
||||
type Props = {| children: React$Node |};
|
||||
|
||||
// TODO Remove TreeContextController wrapper element once global ConsearchText.write API exists.
|
||||
function TreeContextController({ children, viewElementSource }: Props) {
|
||||
function TreeContextController({ children }: Props) {
|
||||
const bridge = useContext(BridgeContext);
|
||||
const store = useContext(StoreContext);
|
||||
|
||||
@@ -662,129 +691,20 @@ function TreeContextController({ children, viewElementSource }: Props) {
|
||||
// Owners
|
||||
ownerStack: [],
|
||||
ownerStackIndex: null,
|
||||
_ownerFlatTree: null,
|
||||
ownerFlatTree: null,
|
||||
|
||||
// Inspection element panel
|
||||
inspectedElementID: null,
|
||||
});
|
||||
|
||||
const dispatchWrapper = useCallback(
|
||||
params => {
|
||||
dispatch(params);
|
||||
(action: Action) => {
|
||||
dispatch(action);
|
||||
next(() => dispatch({ type: 'UPDATE_INSPECTED_ELEMENT_ID' }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const getElementAtIndex = useCallback(
|
||||
(index: number) => {
|
||||
return state._ownerFlatTree === null
|
||||
? store.getElementAtIndex(index)
|
||||
: store.getElementByID(state._ownerFlatTree[index]);
|
||||
},
|
||||
[state, store]
|
||||
);
|
||||
const selectElementAtIndex = useCallback(
|
||||
(index: number) =>
|
||||
dispatchWrapper({ type: 'SELECT_ELEMENT_AT_INDEX', payload: index }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const selectElementByID = useCallback(
|
||||
(id: number | null) =>
|
||||
dispatchWrapper({ type: 'SELECT_ELEMENT_BY_ID', payload: id }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const setSearchText = useCallback(
|
||||
(text: string) =>
|
||||
dispatchWrapper({ type: 'SET_SEARCH_TEXT', payload: text }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const goToNextSearchResult = useCallback(
|
||||
() => dispatchWrapper({ type: 'GO_TO_NEXT_SEARCH_RESULT' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const goToPreviousSearchResult = useCallback(
|
||||
() => dispatchWrapper({ type: 'GO_TO_PREVIOUS_SEARCH_RESULT' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const resetOwnerStack = useCallback(
|
||||
() => dispatchWrapper({ type: 'RESET_OWNER_STACK' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const selectChildElementInTree = useCallback(
|
||||
() => dispatchWrapper({ type: 'SELECT_CHILD_ELEMENT_IN_TREE' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const selectNextElementInTree = useCallback(
|
||||
() => dispatchWrapper({ type: 'SELECT_NEXT_ELEMENT_IN_TREE' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const selectParentElementInTree = useCallback(
|
||||
() => dispatchWrapper({ type: 'SELECT_PARENT_ELEMENT_IN_TREE' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const selectPreviousElementInTree = useCallback(
|
||||
() => dispatchWrapper({ type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE' }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
const selectOwner = useCallback(
|
||||
(id: number) => dispatchWrapper({ type: 'SELECT_OWNER', payload: id }),
|
||||
[dispatchWrapper]
|
||||
);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
// Tree (derived from Store or owners state)
|
||||
baseDepth: state.baseDepth,
|
||||
numElements: state.numElements,
|
||||
selectedElementID: state.selectedElementID,
|
||||
selectedElementIndex: state.selectedElementIndex,
|
||||
getElementAtIndex,
|
||||
selectChildElementInTree,
|
||||
selectElementByID,
|
||||
selectElementAtIndex,
|
||||
selectNextElementInTree,
|
||||
selectParentElementInTree,
|
||||
selectPreviousElementInTree,
|
||||
|
||||
// Search
|
||||
searchIndex: state.searchIndex,
|
||||
searchResults: state.searchResults,
|
||||
searchText: state.searchText,
|
||||
setSearchText,
|
||||
goToNextSearchResult,
|
||||
goToPreviousSearchResult,
|
||||
|
||||
// Owners
|
||||
ownerStack: state.ownerStack,
|
||||
ownerStackIndex: state.ownerStackIndex,
|
||||
resetOwnerStack,
|
||||
selectOwner,
|
||||
|
||||
// Inspection element panel
|
||||
inspectedElementID: state.inspectedElementID,
|
||||
|
||||
// Injected by parent HTML/JavaScript
|
||||
viewElementSource,
|
||||
}),
|
||||
[
|
||||
getElementAtIndex,
|
||||
goToNextSearchResult,
|
||||
goToPreviousSearchResult,
|
||||
resetOwnerStack,
|
||||
selectChildElementInTree,
|
||||
selectElementAtIndex,
|
||||
selectElementByID,
|
||||
selectNextElementInTree,
|
||||
selectParentElementInTree,
|
||||
selectOwner,
|
||||
selectPreviousElementInTree,
|
||||
setSearchText,
|
||||
state,
|
||||
viewElementSource,
|
||||
]
|
||||
);
|
||||
|
||||
// Listen for host element selections.
|
||||
useEffect(() => {
|
||||
const handleSelectFiber = (id: number) =>
|
||||
@@ -837,7 +757,13 @@ function TreeContextController({ children, viewElementSource }: Props) {
|
||||
return () => store.removeListener('mutated', handleStoreMutated);
|
||||
}, [dispatchWrapper, initialRevision, store]);
|
||||
|
||||
return <TreeContext.Provider value={value}>{children}</TreeContext.Provider>;
|
||||
return (
|
||||
<TreeStateContext.Provider value={state}>
|
||||
<TreeDispatcherContext.Provider value={dispatchWrapper}>
|
||||
{children}
|
||||
</TreeDispatcherContext.Provider>
|
||||
</TreeStateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function calculateCurrentOwnerList(
|
||||
@@ -886,4 +812,4 @@ function recursivelySearchTree(
|
||||
);
|
||||
}
|
||||
|
||||
export { TreeContext, TreeContextController };
|
||||
export { TreeDispatcherContext, TreeStateContext, TreeContextController };
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import { createContext } from 'react';
|
||||
|
||||
const ViewElementSourceContext = createContext<Function | null>(null);
|
||||
ViewElementSourceContext.displayName = 'ViewElementSourceContext';
|
||||
|
||||
export default ViewElementSourceContext;
|
||||
@@ -14,6 +14,7 @@ import Settings from './Settings/Settings';
|
||||
import TabBar from './TabBar';
|
||||
import { SettingsContextController } from './Settings/SettingsContext';
|
||||
import { TreeContextController } from './Components/TreeContext';
|
||||
import ViewElementSourceContext from './Components/ViewElementSourceContext';
|
||||
import { ProfilerContextController } from './Profiler/ProfilerContext';
|
||||
import ReactLogo from './ReactLogo';
|
||||
|
||||
@@ -121,47 +122,55 @@ export default function DevTools({
|
||||
profilerPortalContainer={profilerPortalContainer}
|
||||
settingsPortalContainer={settingsPortalContainer}
|
||||
>
|
||||
<TreeContextController viewElementSource={viewElementSource}>
|
||||
<ProfilerContextController>
|
||||
<div className={styles.DevTools}>
|
||||
{showTabBar && (
|
||||
<div className={styles.TabBar}>
|
||||
<ReactLogo />
|
||||
<span className={styles.DevToolsVersion}>
|
||||
{process.env.DEVTOOLS_VERSION}
|
||||
</span>
|
||||
<div className={styles.Spacer} />
|
||||
<TabBar
|
||||
currentTab={tab}
|
||||
id="DevTools"
|
||||
selectTab={setTab}
|
||||
size="large"
|
||||
tabs={
|
||||
supportsProfiling
|
||||
? tabsWithProfiler
|
||||
: tabsWithoutProfiler
|
||||
}
|
||||
<ViewElementSourceContext.Provider value={viewElementSource}>
|
||||
<TreeContextController>
|
||||
<ProfilerContextController>
|
||||
<div className={styles.DevTools}>
|
||||
{showTabBar && (
|
||||
<div className={styles.TabBar}>
|
||||
<ReactLogo />
|
||||
<span className={styles.DevToolsVersion}>
|
||||
{process.env.DEVTOOLS_VERSION}
|
||||
</span>
|
||||
<div className={styles.Spacer} />
|
||||
<TabBar
|
||||
currentTab={tab}
|
||||
id="DevTools"
|
||||
selectTab={setTab}
|
||||
size="large"
|
||||
tabs={
|
||||
supportsProfiling
|
||||
? tabsWithProfiler
|
||||
: tabsWithoutProfiler
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'components'}
|
||||
>
|
||||
<Components portalContainer={componentsPortalContainer} />
|
||||
</div>
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'profiler'}
|
||||
>
|
||||
<Profiler
|
||||
portalContainer={profilerPortalContainer}
|
||||
supportsProfiling={supportsProfiling}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'components'}
|
||||
>
|
||||
<Components portalContainer={componentsPortalContainer} />
|
||||
<div
|
||||
className={styles.TabContent}
|
||||
hidden={tab !== 'settings'}
|
||||
>
|
||||
<Settings portalContainer={settingsPortalContainer} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.TabContent} hidden={tab !== 'profiler'}>
|
||||
<Profiler
|
||||
portalContainer={profilerPortalContainer}
|
||||
supportsProfiling={supportsProfiling}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.TabContent} hidden={tab !== 'settings'}>
|
||||
<Settings portalContainer={settingsPortalContainer} />
|
||||
</div>
|
||||
</div>
|
||||
</ProfilerContextController>
|
||||
</TreeContextController>
|
||||
</ProfilerContextController>
|
||||
</TreeContextController>
|
||||
</ViewElementSourceContext.Provider>
|
||||
</SettingsContextController>
|
||||
</StoreContext.Provider>
|
||||
</BridgeContext.Provider>
|
||||
|
||||
@@ -9,7 +9,10 @@ import React, {
|
||||
} from 'react';
|
||||
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
|
||||
import { useLocalStorage, useSubscription } from '../hooks';
|
||||
import { TreeContext } from '../Components/TreeContext';
|
||||
import {
|
||||
TreeDispatcherContext,
|
||||
TreeStateContext,
|
||||
} from '../Components/TreeContext';
|
||||
import { StoreContext } from '../context';
|
||||
import Store from '../../store';
|
||||
|
||||
@@ -79,7 +82,8 @@ type Props = {|
|
||||
|
||||
function ProfilerContextController({ children }: Props) {
|
||||
const store = useContext(StoreContext);
|
||||
const { selectElementByID, selectedElementID } = useContext(TreeContext);
|
||||
const { selectedElementID } = useContext(TreeStateContext);
|
||||
const dispatch = useContext(TreeDispatcherContext);
|
||||
|
||||
const subscription = useMemo(
|
||||
() => ({
|
||||
@@ -155,11 +159,14 @@ function ProfilerContextController({ children }: Props) {
|
||||
// If this element is still in the store, then select it in the Components tab as well.
|
||||
const element = store.getElementByID(id);
|
||||
if (element !== null) {
|
||||
selectElementByID(id);
|
||||
dispatch({
|
||||
type: 'SELECT_ELEMENT_BY_ID',
|
||||
payload: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[selectElementByID, selectFiberID, selectFiberName, store]
|
||||
[dispatch, selectFiberID, selectFiberName, store]
|
||||
);
|
||||
|
||||
if (isProfiling) {
|
||||
|
||||
Reference in New Issue
Block a user