@samitouri / QOS-React / commits / f92d2bbf83

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