Fixed editing props for memo and forwardRef components
Brian Vaughn committed
Apr 3, 2019 at 09:29 UTC
f92d2bbf83360b358ecf08c0db85cff6fd08cbff
6 files changed
+77
-23
shells/dev/app/EditableProps/index.js
+39
-8
@@ -3,7 +3,9 @@
3
import React, {
4
createContext,
5
Component,
6
+ forwardRef,
7
Fragment,
8
+ memo,
9
useCallback,
10
useDebugValue,
11
useEffect,
@@ -38,7 +40,7 @@ function StatefulFunction({ name }: StatefulFunctionProps) {
40
);
41
42
return (
41
- <Fragment>
43
+ <ul>
44
<li>Name: {name}</li>
45
<li>
46
<button onClick={handleUpdateCountClick}>
@@ -51,7 +53,7 @@ function StatefulFunction({ name }: StatefulFunctionProps) {
53
<li>
54
<button onClick={handleUpdateReducerClick}>Swap reducer values</button>
55
</li>
54
- </Fragment>
56
+ </ul>
57
);
58
}
59
@@ -76,7 +78,7 @@ class StatefulClass extends Component<Props, State> {
78
79
render() {
80
return (
79
- <Fragment>
81
+ <ul>
82
<li>Name: {this.props.name}</li>
83
<li>Toggle: {this.props.toggle ? 'true' : 'false'}</li>
84
<li>
@@ -84,19 +86,48 @@ class StatefulClass extends Component<Props, State> {
86
</li>
87
<li>Cities: {this.state.cities.join(', ')}</li>
88
<li>Context: {this.context ? 'true' : 'false'}</li>
87
- </Fragment>
89
+ </ul>
90
);
91
}
92
}
93
94
+const MemoizedStatefulClass = memo(StatefulClass);
95
+const MemoizedStatefulFunction = memo(StatefulFunction);
96
+
97
+const ForwardRef = forwardRef<{| name: string |}, HTMLUListElement>(
98
+ ({ name }, ref) => {
99
+ const [count, updateCount] = useState(0);
100
+ const debouncedCount = useDebounce(count, 1000);
101
+ const handleUpdateCountClick = useCallback(() => updateCount(count + 1), [
102
+ count,
103
+ ]);
104
+ return (
105
+ <ul ref={ref}>
106
+ <li>Name: {name}</li>
107
+ <li>
108
+ <button onClick={handleUpdateCountClick}>
109
+ Debounced count: {debouncedCount}
110
+ </button>
111
+ </li>
112
+ </ul>
113
+ );
114
+ }
115
+);
116
+
117
export default function EditableProps() {
118
return (
119
<Fragment>
120
<h1>Editable props</h1>
96
- <ul>
97
- <StatefulClass name="Brian" toggle={true} />
98
- <StatefulFunction name="Brian" />
99
- </ul>
121
+ <strong>Class</strong>
122
+ <StatefulClass name="Brian" toggle={true} />
123
+ <strong>Function</strong>
124
+ <StatefulFunction name="Brian" />
125
+ <strong>Memoized Class</strong>
126
+ <MemoizedStatefulClass name="Brian" toggle={true} />
127
+ <strong>Memoized Function</strong>
128
+ <MemoizedStatefulFunction name="Brian" />
129
+ <strong>Forward Ref</strong>
130
+ <ForwardRef name="Brian" />
131
</Fragment>
132
);
133
}
src/devtools/views/Components/HooksTree.css
+5
@@ -25,6 +25,11 @@
25
color: var(--color-dim);
26
flex: 0 0 auto;
27
}
28
+.EditableName {
29
+ color: var(--color-attribute-name);
30
+ flex: 0 0 auto;
31
+}
32
+.EditableName:after,
33
.Name:after {
34
color: var(--color-text-color);
35
content: ': ';
src/devtools/views/Components/HooksTree.js
+10
-8
@@ -110,12 +110,7 @@ function HookView({ canEditHooks, hook, id, path = [] }: HookViewProps) {
110
<div className={styles.NameValueRow}>
111
<span className={styles.Name}>{name}</span>
112
</div>
113
- <KeyValue
114
- depth={1}
115
- name="DebugValue"
116
- nameClassName={styles.Name}
117
- value={value}
118
- />
113
+ <KeyValue depth={1} name="DebugValue" value={value} />
114
<InnerHooksTreeView
115
canEditHooks={canEditHooks}
116
hooks={subHooks}
@@ -160,7 +155,6 @@ function HookView({ canEditHooks, hook, id, path = [] }: HookViewProps) {
155
<KeyValue
156
depth={0}
157
name={name}
163
- nameClassName={styles.Name}
158
overrideValueFn={overrideValueFn}
159
value={value}
160
/>
@@ -170,7 +164,15 @@ function HookView({ canEditHooks, hook, id, path = [] }: HookViewProps) {
164
return (
165
<div className={styles.Hook}>
166
<div className={styles.NameValueRow}>
173
- <span className={styles.Name}>{name}</span>
167
+ <span
168
+ className={
169
+ typeof overrideValueFn === 'function'
170
+ ? styles.EditableName
171
+ : styles.Name
172
+ }
173
+ >
174
+ {name}
175
+ </span>
176
{typeof overrideValueFn === 'function' ? (
177
<EditableValue
178
dataType={type}
src/devtools/views/Components/KeyValue.css
+5
@@ -3,9 +3,14 @@
3
}
4
5
.Name {
6
+ color: var(--color-dim);
7
+ flex: 0 0 auto;
8
+}
9
+.EditableName {
10
color: var(--color-attribute-name);
11
flex: 0 0 auto;
12
}
13
+.EditableName:after,
14
.Name:after {
15
content: ': ';
16
color: var(--color-text-color);
src/devtools/views/Components/KeyValue.js
+6
-5
@@ -11,7 +11,6 @@ type OverrideValueFn = (path: Array<string | number>, value: any) => void;
11
type KeyValueProps = {|
12
depth: number,
13
name: string,
14
- nameClassName?: string,
14
overrideValueFn?: ?OverrideValueFn,
15
path?: Array<any>,
16
value: any,
@@ -20,7 +19,6 @@ type KeyValueProps = {|
19
export default function KeyValue({
20
depth,
21
name,
23
- nameClassName = styles.Name,
22
overrideValueFn,
23
path = [],
24
value,
@@ -47,6 +45,9 @@ export default function KeyValue({
45
displayValue = 'undefined';
46
}
47
48
+ const nameClassName =
49
+ typeof overrideValueFn === 'function' ? styles.EditableName : styles.Name;
50
+
51
children = (
52
<div key="root" className={styles.Item} style={{ paddingLeft }}>
53
<span className={nameClassName}>{name}</span>
@@ -66,7 +67,7 @@ export default function KeyValue({
67
// TODO Is this type even necessary? Can we just drop it?
68
children = (
69
<div key="root" className={styles.Item} style={{ paddingLeft }}>
69
- <span className={nameClassName}>{name}</span>
70
+ <span className={styles.Name}>{name}</span>
71
<span className={styles.Value}>{getMetaValueLabel(value)}</span>
72
</div>
73
);
@@ -88,7 +89,7 @@ export default function KeyValue({
89
className={styles.Item}
90
style={{ paddingLeft }}
91
>
91
- <span className={nameClassName}>{name}</span>
92
+ <span className={styles.Name}>{name}</span>
93
<span>Array</span>
94
</div>
95
);
@@ -110,7 +111,7 @@ export default function KeyValue({
111
className={styles.Item}
112
style={{ paddingLeft }}
113
>
113
- <span className={nameClassName}>{name}</span>
114
+ <span className={styles.Name}>{name}</span>
115
<span>Object</span>
116
</div>
117
);
src/devtools/views/Components/SelectedElement.js
+12
-2
@@ -15,7 +15,12 @@ import HooksTree from './HooksTree';
15
import InspectedElementTree from './InspectedElementTree';
16
import { hydrate } from 'src/hydration';
17
import styles from './SelectedElement.css';
18
-import { ElementTypeClass, ElementTypeFunction } from '../../types';
18
+import {
19
+ ElementTypeClass,
20
+ ElementTypeForwardRef,
21
+ ElementTypeFunction,
22
+ ElementTypeMemo,
23
+} from '../../types';
24
25
import type { InspectedElement } from './types';
26
import type { DehydratedData, Element } from './types';
@@ -145,7 +150,12 @@ function InspectedElementView({
150
const rendererID = store.getRendererIDForElement(id);
151
bridge.send('overrideState', { id, path, rendererID, value });
152
};
148
- } else if (type === ElementTypeFunction && canEditFunctionProps) {
153
+ } else if (
154
+ (type === ElementTypeFunction ||
155
+ type === ElementTypeMemo ||
156
+ type === ElementTypeForwardRef) &&
157
+ canEditFunctionProps
158
+ ) {
159
overridePropsFn = (path: Array<string | number>, value: any) => {
160
const rendererID = store.getRendererIDForElement(id);
161
bridge.send('overrideProps', { id, path, rendererID, value });