@samitouri / QOS-React / commits / decff26e81

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 });