Add a warning for adding some properties in the SyntheticEvent object if Proxy is supported
Toru Kobayashi committed
Feb 18, 2016 at 18:41 UTC
decff26e818b71dfd785e11e2b75fdddf2efd1ec
12 files changed
+78
-15
src/renderers/dom/client/syntheticEvents/SyntheticClipboardEvent.js
+1
-1
@@ -34,7 +34,7 @@ var ClipboardEventInterface = {
34
* @extends {SyntheticUIEvent}
35
*/
36
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
37
- SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
37
+ return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
38
}
39
40
SyntheticEvent.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticCompositionEvent.js
+1
-1
@@ -33,7 +33,7 @@ function SyntheticCompositionEvent(
33
nativeEvent,
34
nativeEventTarget
35
) {
36
- SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
36
+ return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
37
}
38
39
SyntheticEvent.augmentClass(
src/renderers/dom/client/syntheticEvents/SyntheticDragEvent.js
+1
-1
@@ -28,7 +28,7 @@ var DragEventInterface = {
28
* @extends {SyntheticUIEvent}
29
*/
30
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
31
- SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
31
+ return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
32
}
33
34
SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
+49
-5
@@ -17,6 +17,19 @@ var assign = require('Object.assign');
17
var emptyFunction = require('emptyFunction');
18
var warning = require('warning');
19
20
+var didWarnForAddedNewProperty = false;
21
+var isProxySupported = typeof Proxy === 'function';
22
+
23
+var shouldBeReleasedProperties = [
24
+ 'dispatchConfig',
25
+ '_targetInst',
26
+ 'nativeEvent',
27
+ 'isDefaultPrevented',
28
+ 'isPropagationStopped',
29
+ '_dispatchListeners',
30
+ '_dispatchInstances',
31
+];
32
+
33
/**
34
* @interface Event
35
* @see http://www.w3.org/TR/DOM-Level-3-Events/
@@ -57,7 +70,7 @@ var EventInterface = {
70
function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
71
if (__DEV__) {
72
// these have a getter/setter for warnings
60
- delete this.nativeEvent;
73
+ delete this.nativeEvent;
74
delete this.preventDefault;
75
delete this.stopPropagation;
76
}
@@ -95,6 +108,7 @@ function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarg
108
this.isDefaultPrevented = emptyFunction.thatReturnsFalse;
109
}
110
this.isPropagationStopped = emptyFunction.thatReturnsFalse;
111
+ return this;
112
}
113
114
assign(SyntheticEvent.prototype, {
@@ -156,22 +170,52 @@ assign(SyntheticEvent.prototype, {
170
this[propName] = null;
171
}
172
}
173
+ for (var i = 0; i < shouldBeReleasedProperties.length; i++) {
174
+ this[shouldBeReleasedProperties[i]] = null;
175
+ }
176
if (__DEV__) {
177
var noop = require('emptyFunction');
178
Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null));
179
Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', noop));
180
Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', noop));
164
- } else {
165
- this.nativeEvent = null;
181
}
167
- this.dispatchConfig = null;
168
- this._targetInst = null;
182
},
183
184
});
185
186
SyntheticEvent.Interface = EventInterface;
187
188
+if (__DEV__) {
189
+ if (isProxySupported) {
190
+ /*eslint-disable no-func-assign */
191
+ SyntheticEvent = new Proxy(SyntheticEvent, {
192
+ construct: function(target, args) {
193
+ return this.apply(target, {}, args);
194
+ },
195
+ apply: function(constructor, that, args) {
196
+ return new Proxy(constructor.apply(that, args), {
197
+ set: function(target, prop, value) {
198
+ if (prop !== 'isPersistent' &&
199
+ !target.constructor.Interface.hasOwnProperty(prop) &&
200
+ shouldBeReleasedProperties.indexOf(prop) === -1) {
201
+ warning(
202
+ didWarnForAddedNewProperty || target.isPersistent(),
203
+ 'This synthetic event is reused for performance reasons. If you\'re ' +
204
+ 'seeing this, you\'re adding a new property in the synthetic event object. ' +
205
+ 'The property is never released. See ' +
206
+ 'https://fb.me/react-event-pooling for more information.'
207
+ );
208
+ didWarnForAddedNewProperty = true;
209
+ }
210
+ target[prop] = value;
211
+ return true;
212
+ },
213
+ });
214
+ },
215
+ });
216
+ /*eslint-enable no-func-assign */
217
+ }
218
+}
219
/**
220
* Helper to reduce boilerplate when creating subclasses.
221
*
src/renderers/dom/client/syntheticEvents/SyntheticFocusEvent.js
+1
-1
@@ -28,7 +28,7 @@ var FocusEventInterface = {
28
* @extends {SyntheticUIEvent}
29
*/
30
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
31
- SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
31
+ return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
32
}
33
34
SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticInputEvent.js
+1
-1
@@ -34,7 +34,7 @@ function SyntheticInputEvent(
34
nativeEvent,
35
nativeEventTarget
36
) {
37
- SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
37
+ return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
38
}
39
40
SyntheticEvent.augmentClass(
src/renderers/dom/client/syntheticEvents/SyntheticKeyboardEvent.js
+1
-1
@@ -76,7 +76,7 @@ var KeyboardEventInterface = {
76
* @extends {SyntheticUIEvent}
77
*/
78
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
79
- SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
79
+ return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
80
}
81
82
SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticMouseEvent.js
+1
-1
@@ -72,7 +72,7 @@ var MouseEventInterface = {
72
* @extends {SyntheticUIEvent}
73
*/
74
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
75
- SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
75
+ return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
76
}
77
78
SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticTouchEvent.js
+1
-1
@@ -37,7 +37,7 @@ var TouchEventInterface = {
37
* @extends {SyntheticUIEvent}
38
*/
39
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
40
- SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
40
+ return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
41
}
42
43
SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticUIEvent.js
+1
-1
@@ -51,7 +51,7 @@ var UIEventInterface = {
51
* @extends {SyntheticEvent}
52
*/
53
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
54
- SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
54
+ return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
55
}
56
57
SyntheticEvent.augmentClass(SyntheticUIEvent, UIEventInterface);
src/renderers/dom/client/syntheticEvents/SyntheticWheelEvent.js
+1
-1
@@ -50,7 +50,7 @@ var WheelEventInterface = {
50
* @extends {SyntheticMouseEvent}
51
*/
52
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
53
- SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
53
+ return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
54
}
55
56
SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface);
src/renderers/dom/client/syntheticEvents/__tests__/SyntheticEvent-test.js
+19
@@ -160,4 +160,23 @@ describe('SyntheticEvent', function() {
160
'See https://fb.me/react-event-pooling for more information.'
161
);
162
});
163
+
164
+ it('should warn if Proxy is supported and the synthetic event is added a property', function() {
165
+ spyOn(console, 'error');
166
+ var syntheticEvent = createEvent({});
167
+ syntheticEvent.foo = 'bar';
168
+ SyntheticEvent.release(syntheticEvent);
169
+ expect(syntheticEvent.foo).toBe('bar');
170
+ if (typeof Proxy === 'function') {
171
+ expect(console.error.calls.length).toBe(1);
172
+ expect(console.error.argsForCall[0][0]).toBe(
173
+ 'Warning: This synthetic event is reused for performance reasons. If you\'re ' +
174
+ 'seeing this, you\'re adding a new property in the synthetic event object. ' +
175
+ 'The property is never released. See ' +
176
+ 'https://fb.me/react-event-pooling for more information.'
177
+ );
178
+ } else {
179
+ expect(console.error.calls.length).toBe(0);
180
+ }
181
+ });
182
});