@samitouri / QOS-React / commits / 2ac815784b

Removed unnecessary layout effect in favor of a setState

Brian Vaughn committed Apr 7, 2019 at 15:19 UTC 2ac815784b9aff97fb367d605c126d55846cecc7
2 files changed +117 -129
src/devtools/views/Components/OwnersStack.css
+14 -31
@@ -5,7 +5,7 @@
5 }
6
7 .Component,
8 -.FocusedComponent {
8 +.SelectedComponent {
9 padding: 0.25rem;
10 margin-right: 0.5rem;
11 color: var(--color-component-name);
@@ -14,11 +14,8 @@
14 border-radius: 0.125rem;
15 border: none;
16 background: none;
17 -}
18 -
19 -.Component {
17 cursor: pointer;
21 - color: var(--color-component-name);
18 + text-align: left;
19 }
20 .Component:hover {
21 background-color: var(--color-hover-background);
@@ -28,52 +25,38 @@
25 background-color: var(--color-hover-background);
26 }
27
31 -.FocusedComponent {
28 +.SelectedComponent {
29 background-color: var(--color-selected-background);
30 color: var(--color-selected-foreground);
31 }
35 -.FocusedComponent:focus {
32 +.SelectedComponent:focus {
33 outline: none;
34 }
35
39 -.ElementsBar {
36 +.Bar {
37 + display: flex;
38 flex: 1 0 auto;
39 }
40
43 -.ElementsBarSelectedOnly {
44 - margin-left: 0.25rem;
45 -}
46 -
47 -.ElementsBarSelectedOnly .Component {
48 - visibility: hidden;
49 -}
50 -.ElementsBarSelectedOnly .FocusedComponent {
51 - float: left;
41 +.Toggle {
42 + margin-right: 0.5rem;
43 }
44
54 -.ElementsDropdown {
45 +.DropdownWrapper {
46 + display: inline-block;
47 position: relative;
48 }
49
50 .Dropdown {
51 z-index: 1;
52 position: absolute;
61 - top: calc(100% + 5px);
53 + top: calc(100% + 0.25rem);
54 left: 0;
55 background-color: var(--color-background);
64 - border: 1px solid var(--color-selected-border);
56 overflow-y: auto;
66 -}
67 -
68 -.Dropdown .Component,
69 -.Dropdown .FocusedComponent {
70 - display: block;
71 - margin: 0.25rem 0.75rem 0.35rem;
72 -}
73 -
74 -.DropdownButtonActive {
75 - background-color: var(--color-selected-background);
76 - color: var(--color-selected-foreground);
57 + max-height: 10rem;
58 + display: flex;
59 + flex-direction: column;
60 }
61
62 .VRule {
src/devtools/views/Components/OwnersStack.js
+103 -98
@@ -3,13 +3,12 @@ import React, {
3 useCallback,
4 useContext,
5 useLayoutEffect,
6 + useRef,
7 useState,
7 - createRef,
8 - forwardRef,
8 } from 'react';
10 -import classNames from 'classnames';
9 import Button from '../Button';
10 import ButtonIcon from '../ButtonIcon';
11 +import Toggle from '../Toggle';
12 import { TreeContext } from './TreeContext';
13 import { StoreContext } from '../context';
14 import { useIsOverflowing } from '../hooks';
@@ -18,67 +17,129 @@ import type { Element } from './types';
17
18 import styles from './OwnersStack.css';
19
20 +export default function OwnerStack() {
21 + const { ownerStack, ownerStackIndex, resetOwnerStack } = useContext(
22 + TreeContext
23 + );
24 +
25 + const [elementsTotalWidth, setElementsTotalWidth] = useState(0);
26 + const elementsBarRef = useRef<HTMLDivElement | null>(null);
27 + const isOverflowing = useIsOverflowing(elementsBarRef, elementsTotalWidth);
28 +
29 + useLayoutEffect(() => {
30 + if (elementsBarRef.current === null) {
31 + return () => {};
32 + }
33 +
34 + let elementsTotalWidth = 0;
35 + for (let i = 0; i < ownerStack.length; i++) {
36 + const element = elementsBarRef.current.children[i];
37 + const computedStyle = getComputedStyle(element);
38 +
39 + elementsTotalWidth +=
40 + element.offsetWidth +
41 + parseInt(computedStyle.marginLeft, 10) +
42 + parseInt(computedStyle.marginRight, 10);
43 + }
44 +
45 + setElementsTotalWidth(elementsTotalWidth);
46 + }, [elementsBarRef, ownerStack.length]);
47 +
48 + return (
49 + <div className={styles.OwnerStack}>
50 + <Button
51 + className={styles.IconButton}
52 + onClick={resetOwnerStack}
53 + title="Back to tree view"
54 + >
55 + <ButtonIcon type="close" />
56 + </Button>
57 + <div className={styles.VRule} />
58 + <div className={styles.Bar} ref={elementsBarRef}>
59 + {isOverflowing && (
60 + <ElementsDropdown
61 + ownerStack={ownerStack}
62 + ownerStackIndex={ownerStackIndex}
63 + />
64 + )}
65 + {isOverflowing ? (
66 + <ElementView
67 + id={ownerStack[((ownerStackIndex: any): number)]}
68 + index={ownerStackIndex}
69 + />
70 + ) : (
71 + ownerStack.map((id, index) => (
72 + <ElementView key={id} id={id} index={index} />
73 + ))
74 + )}
75 + </div>
76 + </div>
77 + );
78 +}
79 +
80 type ElementsDropdownProps = {
22 - selectedElementIndex: number | null,
23 - children: Array<any>,
81 + ownerStack: Array<number>,
82 + ownerStackIndex: number | null,
83 };
84 function ElementsDropdown({
26 - selectedElementIndex,
27 - children,
85 + ownerStack,
86 + ownerStackIndex,
87 }: ElementsDropdownProps) {
88 + const store = useContext(StoreContext);
89 + const { selectOwner } = useContext(TreeContext);
90 +
91 const [isDropdownVisible, setIsDropdownVisible] = useState(false);
92
31 - const handleClick = useCallback(() => {
93 + const handleDropdownButtonClick = useCallback(() => {
94 setIsDropdownVisible(!isDropdownVisible);
95 }, [isDropdownVisible, setIsDropdownVisible]);
96
35 - useLayoutEffect(() => {
36 - setIsDropdownVisible(false);
37 - // eslint-disable-next-line react-hooks/exhaustive-deps
38 - }, [selectedElementIndex]);
97 + const handleElementClick = useCallback(
98 + (id: number) => {
99 + selectOwner(id);
100 + setIsDropdownVisible(false);
101 + },
102 + [selectOwner, setIsDropdownVisible]
103 + );
104
105 return (
41 - <div className={styles.ElementsDropdown}>
42 - <Button
43 - className={classNames(styles.IconButton, {
44 - [styles.DropdownButtonActive]: isDropdownVisible,
45 - })}
46 - onClick={handleClick}
106 + <div className={styles.DropdownWrapper}>
107 + <Toggle
108 + className={styles.Toggle}
109 + isChecked={isDropdownVisible}
110 + onChange={handleDropdownButtonClick}
111 title="Open elements dropdown"
112 >
113 <ButtonIcon type="more" />
50 - </Button>
51 - {isDropdownVisible && <div className={styles.Dropdown}>{children}</div>}
114 + </Toggle>
115 + {isDropdownVisible && (
116 + <div className={styles.Dropdown}>
117 + {ownerStack.map((id, index) => (
118 + <button
119 + className={
120 + ownerStackIndex === index
121 + ? styles.SelectedComponent
122 + : styles.Component
123 + }
124 + onClick={() => handleElementClick(id)}
125 + >
126 + {((store.getElementByID(id): any): Element).displayName}
127 + </button>
128 + ))}
129 + </div>
130 + )}
131 </div>
132 );
133 }
134
56 -type ElementsBarProps = {
57 - elements: Array<any>,
58 - showSelectedOnly: boolean,
59 -};
60 -const ElementsBar = forwardRef(
61 - ({ elements, showSelectedOnly }: ElementsBarProps, ref: Object) => {
62 - return (
63 - <div
64 - className={classNames(styles.ElementsBar, {
65 - [styles.ElementsBarSelectedOnly]: showSelectedOnly,
66 - })}
67 - ref={ref}
68 - >
69 - {elements}
70 - </div>
71 - );
72 - }
73 -);
74 -
135 type ElementViewProps = {
136 id: number,
77 - index: number,
137 + index: number | null,
138 };
139 function ElementView({ id, index }: ElementViewProps) {
80 - const { ownerStackIndex, selectOwner } = useContext(TreeContext);
140 const store = useContext(StoreContext);
141 + const { ownerStackIndex, selectOwner } = useContext(TreeContext);
142 +
143 const { displayName } = ((store.getElementByID(id): any): Element);
144
145 const isSelected = ownerStackIndex === index;
@@ -91,66 +152,10 @@ function ElementView({ id, index }: ElementViewProps) {
152
153 return (
154 <button
94 - className={isSelected ? styles.FocusedComponent : styles.Component}
155 + className={isSelected ? styles.SelectedComponent : styles.Component}
156 onClick={handleClick}
157 >
158 {displayName}
159 </button>
160 );
161 }
101 -
102 -export default function OwnerStack() {
103 - const { ownerStack, ownerStackIndex, resetOwnerStack } = useContext(
104 - TreeContext
105 - );
106 -
107 - const [elementsTotalWidth, setElementsTotalWidth] = useState(0);
108 - const elementsBarRef = createRef<HTMLDivElement | null>();
109 - const isOverflowing = useIsOverflowing(elementsBarRef, elementsTotalWidth);
110 -
111 - const elements = ownerStack.map((id, index) => (
112 - <ElementView key={id} id={id} index={index} />
113 - ));
114 -
115 - useLayoutEffect(() => {
116 - if (elementsBarRef.current === null) {
117 - return () => {};
118 - }
119 -
120 - let elementsTotalWidth = 0;
121 - for (let i = 0; i < ownerStack.length; i++) {
122 - const element = elementsBarRef.current.children[i];
123 - const computedStyle = getComputedStyle(element);
124 -
125 - elementsTotalWidth +=
126 - element.offsetWidth +
127 - parseInt(computedStyle.marginLeft, 10) +
128 - parseInt(computedStyle.marginRight, 10);
129 - }
130 -
131 - setElementsTotalWidth(elementsTotalWidth);
132 - }, [elementsBarRef, ownerStack.length]);
133 -
134 - return (
135 - <div className={styles.OwnerStack}>
136 - <Button
137 - className={styles.IconButton}
138 - onClick={resetOwnerStack}
139 - title="Back to tree view"
140 - >
141 - <ButtonIcon type="close" />
142 - </Button>
143 - {isOverflowing && (
144 - <ElementsDropdown selectedElementIndex={ownerStackIndex}>
145 - {elements}
146 - </ElementsDropdown>
147 - )}
148 - <div className={styles.VRule} />
149 - <ElementsBar
150 - elements={elements}
151 - showSelectedOnly={isOverflowing}
152 - ref={elementsBarRef}
153 - />
154 - </div>
155 - );
156 -}