[devtools] Fix can't expand prop value in some scenario (#20534)
Co-authored-by: Brian Vaughn <bvaughn@fb.com>
ChenLei committed
Jan 19, 2021 at 23:26 UTC
ba9582da27481677cdac2dd000a438147a5df88e
4 files changed
+144
-5
packages/react-devtools-shared/src/__tests__/inspectedElement-test.js
+77
@@ -1168,6 +1168,83 @@ describe('InspectedElement', () => {
1168
done();
1169
});
1170
1171
+ it('should allow component prop value and value`s prototype has same name params.', async done => {
1172
+ const testData = Object.create(
1173
+ {
1174
+ a: undefined,
1175
+ b: Infinity,
1176
+ c: NaN,
1177
+ d: 'normal',
1178
+ },
1179
+ {
1180
+ a: {
1181
+ value: undefined,
1182
+ writable: true,
1183
+ enumerable: true,
1184
+ configurable: true,
1185
+ },
1186
+ b: {
1187
+ value: Infinity,
1188
+ writable: true,
1189
+ enumerable: true,
1190
+ configurable: true,
1191
+ },
1192
+ c: {
1193
+ value: NaN,
1194
+ writable: true,
1195
+ enumerable: true,
1196
+ configurable: true,
1197
+ },
1198
+ d: {
1199
+ value: 'normal',
1200
+ writable: true,
1201
+ enumerable: true,
1202
+ configurable: true,
1203
+ },
1204
+ },
1205
+ );
1206
+ const Example = ({data}) => null;
1207
+ const container = document.createElement('div');
1208
+ await utils.actAsync(() =>
1209
+ ReactDOM.render(<Example data={testData} />, container),
1210
+ );
1211
+
1212
+ const id = ((store.getElementIDAtIndex(0): any): number);
1213
+
1214
+ let inspectedElement = null;
1215
+
1216
+ function Suspender({target}) {
1217
+ inspectedElement = useInspectedElement(target);
1218
+ return null;
1219
+ }
1220
+
1221
+ await utils.actAsync(
1222
+ () =>
1223
+ TestRenderer.create(
1224
+ <Contexts
1225
+ defaultSelectedElementID={id}
1226
+ defaultSelectedElementIndex={0}>
1227
+ <React.Suspense fallback={null}>
1228
+ <Suspender target={id} />
1229
+ </React.Suspense>
1230
+ </Contexts>,
1231
+ ),
1232
+ false,
1233
+ );
1234
+ expect(inspectedElement.props).toMatchInlineSnapshot(`
1235
+ Object {
1236
+ "data": Object {
1237
+ "a": undefined,
1238
+ "b": Infinity,
1239
+ "c": NaN,
1240
+ "d": "normal",
1241
+ },
1242
+ }
1243
+ `);
1244
+
1245
+ done();
1246
+ });
1247
+
1248
it('should not dehydrate nested values until explicitly requested', async done => {
1249
const Example = () => {
1250
const [state] = React.useState({
packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js
+60
@@ -588,6 +588,66 @@ describe('InspectedElementContext', () => {
588
done();
589
});
590
591
+ it('should allow component prop value and value`s prototype has same name params.', async done => {
592
+ const testData = Object.create(
593
+ {
594
+ a: undefined,
595
+ b: Infinity,
596
+ c: NaN,
597
+ d: 'normal',
598
+ },
599
+ {
600
+ a: {
601
+ value: undefined,
602
+ writable: true,
603
+ enumerable: true,
604
+ configurable: true,
605
+ },
606
+ b: {
607
+ value: Infinity,
608
+ writable: true,
609
+ enumerable: true,
610
+ configurable: true,
611
+ },
612
+ c: {
613
+ value: NaN,
614
+ writable: true,
615
+ enumerable: true,
616
+ configurable: true,
617
+ },
618
+ d: {
619
+ value: 'normal',
620
+ writable: true,
621
+ enumerable: true,
622
+ configurable: true,
623
+ },
624
+ },
625
+ );
626
+
627
+ const Example = ({data}) => null;
628
+ act(() =>
629
+ ReactDOM.render(
630
+ <Example data={testData} />,
631
+ document.createElement('div'),
632
+ ),
633
+ );
634
+
635
+ const id = ((store.getElementIDAtIndex(0): any): number);
636
+ const inspectedElement = await read(id);
637
+
638
+ expect(inspectedElement.props).toMatchInlineSnapshot(`
639
+ Object {
640
+ "data": Object {
641
+ "a": undefined,
642
+ "b": Infinity,
643
+ "c": NaN,
644
+ "d": "normal",
645
+ },
646
+ }
647
+ `);
648
+ done();
649
+ });
650
+
651
it('should not dehydrate nested values until explicitly requested', async done => {
652
const Example = () => null;
653
packages/react-devtools-shared/src/hydration.js
+3
-1
@@ -383,7 +383,9 @@ export function hydrate(
383
384
const value = parent[last];
385
386
- if (value.type === 'infinity') {
386
+ if (!value) {
387
+ return;
388
+ } else if (value.type === 'infinity') {
389
parent[last] = Infinity;
390
} else if (value.type === 'nan') {
391
parent[last] = NaN;
packages/react-devtools-shared/src/utils.js
+4
-4
@@ -70,8 +70,8 @@ export function alphaSortKeys(
70
71
export function getAllEnumerableKeys(
72
obj: Object,
73
-): Array<string | number | Symbol> {
74
- const keys = [];
73
+): Set<string | number | Symbol> {
74
+ const keys = new Set();
75
let current = obj;
76
while (current != null) {
77
const currentKeys = [
@@ -82,7 +82,7 @@ export function getAllEnumerableKeys(
82
currentKeys.forEach(key => {
83
// $FlowFixMe: key can be a Symbol https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
84
if (descriptors[key].enumerable) {
85
- keys.push(key);
85
+ keys.add(key);
86
}
87
});
88
current = Object.getPrototypeOf(current);
@@ -767,7 +767,7 @@ export function formatDataForPreview(
767
return data.toString();
768
case 'object':
769
if (showFormattedValue) {
770
- const keys = getAllEnumerableKeys(data).sort(alphaSortKeys);
770
+ const keys = Array.from(getAllEnumerableKeys(data)).sort(alphaSortKeys);
771
772
let formatted = '';
773
for (let i = 0; i < keys.length; i++) {