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
-}