@samitouri / QOS-React-2 / commits / ba9582da27

[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++) {