Perform hasOwnProperty check in Relay Flight (#20220)
We simulate JSON.stringify in this loop so we should do a has own check. Otherwise we'll include things like constructor properties. This will actually make things throw less even when it should.
Sebastian Markbåge committed
Nov 10, 2020 at 22:59 UTC
12627f93b5357032881412abcc014da53a0b70f8
4 files changed
+61
-12
packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js
+10
-6
@@ -71,6 +71,8 @@ export function processErrorChunk(
71
];
72
}
73
74
+const hasOwnProperty = Object.prototype.hasOwnProperty;
75
+
76
function convertModelToJSON(
77
request: Request,
78
parent: {+[key: string]: ReactModel} | $ReadOnlyArray<ReactModel>,
@@ -88,12 +90,14 @@ function convertModelToJSON(
90
} else {
91
const jsonObj: {[key: string]: JSONValue} = {};
92
for (const nextKey in json) {
91
- jsonObj[nextKey] = convertModelToJSON(
92
- request,
93
- json,
94
- nextKey,
95
- json[nextKey],
96
- );
93
+ if (hasOwnProperty.call(json, nextKey)) {
94
+ jsonObj[nextKey] = convertModelToJSON(
95
+ request,
96
+ json,
97
+ nextKey,
98
+ json[nextKey],
99
+ );
100
+ }
101
}
102
return jsonObj;
103
}
packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js
+19
@@ -214,4 +214,23 @@ describe('ReactFlightDOMRelay', () => {
214
const model = readThrough(transport);
215
expect(model).toEqual(14);
216
});
217
+
218
+ it('should warn in DEV if a class instance polyfill is passed to a host component', () => {
219
+ function Bar() {}
220
+
221
+ function Foo() {}
222
+ Foo.prototype = Object.create(Bar.prototype);
223
+ // This is enumerable which some polyfills do.
224
+ Foo.prototype.constructor = Foo;
225
+ Foo.prototype.method = function() {};
226
+
227
+ expect(() => {
228
+ const transport = [];
229
+ ReactDOMFlightRelayServer.render(<input value={new Foo()} />, transport);
230
+ readThrough(transport);
231
+ }).toErrorDev(
232
+ 'Only plain objects can be passed to client components from server components. ',
233
+ {withoutStack: true},
234
+ );
235
+ });
236
});
packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js
+10
-6
@@ -71,6 +71,8 @@ export function processErrorChunk(
71
];
72
}
73
74
+const hasOwnProperty = Object.prototype.hasOwnProperty;
75
+
76
function convertModelToJSON(
77
request: Request,
78
parent: {+[key: string]: ReactModel} | $ReadOnlyArray<ReactModel>,
@@ -88,12 +90,14 @@ function convertModelToJSON(
90
} else {
91
const jsonObj: {[key: string]: JSONValue} = {};
92
for (const nextKey in json) {
91
- jsonObj[nextKey] = convertModelToJSON(
92
- request,
93
- json,
94
- nextKey,
95
- json[nextKey],
96
- );
93
+ if (hasOwnProperty.call(json, nextKey)) {
94
+ jsonObj[nextKey] = convertModelToJSON(
95
+ request,
96
+ json,
97
+ nextKey,
98
+ json[nextKey],
99
+ );
100
+ }
101
}
102
return jsonObj;
103
}
packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js
+22
@@ -105,4 +105,26 @@ describe('ReactFlightNativeRelay', () => {
105
nativeFabricUIManager.__dumpHierarchyForJestTestsOnly(),
106
).toMatchSnapshot();
107
});
108
+
109
+ it('should warn in DEV if a class instance polyfill is passed to a host component', () => {
110
+ function Bar() {}
111
+
112
+ function Foo() {}
113
+ Foo.prototype = Object.create(Bar.prototype);
114
+ // This is enumerable which some polyfills do.
115
+ Foo.prototype.constructor = Foo;
116
+ Foo.prototype.method = function() {};
117
+
118
+ expect(() => {
119
+ const transport = [];
120
+ ReactNativeFlightRelayServer.render(
121
+ <input value={new Foo()} />,
122
+ transport,
123
+ );
124
+ readThrough(transport);
125
+ }).toErrorDev(
126
+ 'Only plain objects can be passed to client components from server components. ',
127
+ {withoutStack: true},
128
+ );
129
+ });
130
});