Updated hydration logic to handle custom objects
Brian Vaughn committed
Jun 12, 2019 at 15:43 UTC
96f7646a1a0acf0555632ce00e57522b54b1c91e
5 files changed
+102
-6
shells/dev/app/InspectableElements/CustomObject.js
new
+18
@@ -0,0 +1,18 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+class Custom {
6
+ _number = 42;
7
+ get number() {
8
+ return this._number;
9
+ }
10
+}
11
+
12
+export default function CustomObject() {
13
+ return <ChildComponent customObject={new Custom()} />;
14
+}
15
+
16
+function ChildComponent(props: any) {
17
+ return null;
18
+}
shells/dev/app/InspectableElements/InspectableElements.js
+2
@@ -3,6 +3,7 @@
3
import React, { Fragment } from 'react';
4
import Contexts from './Contexts';
5
import CustomHooks from './CustomHooks';
6
+import CustomObject from './CustomObject';
7
import NestedProps from './NestedProps';
8
9
// TODO Add Immutable JS example
@@ -14,6 +15,7 @@ export default function InspectableElements() {
15
<NestedProps />
16
<Contexts />
17
<CustomHooks />
18
+ <CustomObject />
19
</Fragment>
20
);
21
}
src/__tests__/__snapshots__/inspectedElementContext-test.js.snap
+22
@@ -114,3 +114,25 @@ exports[`InspectedElementContext should poll for updates for the currently selec
114
"state": null
115
}
116
`;
117
+
118
+exports[`InspectedElementContext should support custom objects with enumerable properties and getters: 1: mount 1`] = `
119
+[root]
120
+ <Example>
121
+`;
122
+
123
+exports[`InspectedElementContext should support custom objects with enumerable properties and getters: 2: Inspected element 2 1`] = `
124
+{
125
+ "id": 2,
126
+ "owners": null,
127
+ "context": null,
128
+ "events": null,
129
+ "hooks": null,
130
+ "props": {
131
+ "data": {
132
+ "_number": 42,
133
+ "number": 42
134
+ }
135
+ },
136
+ "state": null
137
+}
138
+`;
src/__tests__/inspectedElementContext-test.js
+59
@@ -257,4 +257,63 @@ describe('InspectedElementContext', () => {
257
258
done();
259
});
260
+
261
+ it('should support custom objects with enumerable properties and getters', async done => {
262
+ class CustomData {
263
+ _number = 42;
264
+ get number() {
265
+ return this._number;
266
+ }
267
+ set number(value) {
268
+ this._number = value;
269
+ }
270
+ }
271
+
272
+ const descriptor = ((Object.getOwnPropertyDescriptor(
273
+ CustomData.prototype,
274
+ 'number'
275
+ ): any): PropertyDescriptor<number>);
276
+ descriptor.enumerable = true;
277
+ Object.defineProperty(CustomData.prototype, 'number', descriptor);
278
+
279
+ const Example = ({ data }) => null;
280
+
281
+ const container = document.createElement('div');
282
+ await utils.actAsync(() =>
283
+ ReactDOM.render(<Example data={new CustomData()} />, container)
284
+ );
285
+ expect(store).toMatchSnapshot('1: mount');
286
+
287
+ const example = ((store.getElementAtIndex(0): any): Element);
288
+
289
+ let didFinish = false;
290
+
291
+ function Suspender({ target }) {
292
+ const { read } = React.useContext(InspectedElementContext);
293
+ const inspectedElement = read(target.id);
294
+ expect(inspectedElement).toMatchSnapshot(
295
+ `2: Inspected element ${target.id}`
296
+ );
297
+ didFinish = true;
298
+ return null;
299
+ }
300
+
301
+ await utils.actAsync(
302
+ () =>
303
+ TestRenderer.create(
304
+ <Contexts
305
+ defaultSelectedElementID={example.id}
306
+ defaultSelectedElementIndex={0}
307
+ >
308
+ <React.Suspense fallback={null}>
309
+ <Suspender target={example} />
310
+ </React.Suspense>
311
+ </Contexts>
312
+ ),
313
+ false
314
+ );
315
+ expect(didFinish).toBe(true);
316
+
317
+ done();
318
+ });
319
});
src/hydration.js
+1
-6
@@ -194,12 +194,7 @@ export function dehydrate(
194
},
195
};
196
case 'object':
197
- if (
198
- level > LEVEL_THRESHOLD ||
199
- (data.constructor &&
200
- typeof data.constructor === 'function' &&
201
- data.constructor.name !== 'Object')
202
- ) {
197
+ if (level > LEVEL_THRESHOLD) {
198
return createDehydrated(type, data, cleaned, path);
199
} else {
200
const res = {};