@samitouri / QOS-React / commits / acbb4f93f0

Remove the use of proxies for synthetic events in DEV (#13225)

* Revert #5947 and disable the test * Fix isDefaultPrevented and isPropagationStopped to not get nulled This was a bug introduced by #5947. It's very confusing that they become nulled while stopPropagation/preventDefault don't. * Add a comment * Run Prettier * Fix grammar

Dan Abramov committed Jul 18, 2018 at 00:14 UTC acbb4f93f09ca37f02136f9aa6f7425fde1da2cc
2 files changed +95 -71
packages/events/SyntheticEvent.js
+26 -58
@@ -10,19 +10,8 @@
10 import invariant from 'shared/invariant';
11 import warningWithoutStack from 'shared/warningWithoutStack';
12
13 -let didWarnForAddedNewProperty = false;
13 const EVENT_POOL_SIZE = 10;
14
16 -const shouldBeReleasedProperties = [
17 - 'dispatchConfig',
18 - '_targetInst',
19 - 'nativeEvent',
20 - 'isDefaultPrevented',
21 - 'isPropagationStopped',
22 - '_dispatchListeners',
23 - '_dispatchInstances',
24 -];
25 -
15 /**
16 * @interface Event
17 * @see http://www.w3.org/TR/DOM-Level-3-Events/
@@ -81,6 +70,8 @@ function SyntheticEvent(
70 delete this.nativeEvent;
71 delete this.preventDefault;
72 delete this.stopPropagation;
73 + delete this.isDefaultPrevented;
74 + delete this.isPropagationStopped;
75 }
76
77 this.dispatchConfig = dispatchConfig;
@@ -188,15 +179,35 @@ Object.assign(SyntheticEvent.prototype, {
179 this[propName] = null;
180 }
181 }
191 - for (let i = 0; i < shouldBeReleasedProperties.length; i++) {
192 - this[shouldBeReleasedProperties[i]] = null;
193 - }
182 + this.dispatchConfig = null;
183 + this._targetInst = null;
184 + this.nativeEvent = null;
185 + this.isDefaultPrevented = functionThatReturnsFalse;
186 + this.isPropagationStopped = functionThatReturnsFalse;
187 + this._dispatchListeners = null;
188 + this._dispatchInstances = null;
189 if (__DEV__) {
190 Object.defineProperty(
191 this,
192 'nativeEvent',
193 getPooledWarningPropertyDefinition('nativeEvent', null),
194 );
195 + Object.defineProperty(
196 + this,
197 + 'isDefaultPrevented',
198 + getPooledWarningPropertyDefinition(
199 + 'isDefaultPrevented',
200 + functionThatReturnsFalse,
201 + ),
202 + );
203 + Object.defineProperty(
204 + this,
205 + 'isPropagationStopped',
206 + getPooledWarningPropertyDefinition(
207 + 'isPropagationStopped',
208 + functionThatReturnsFalse,
209 + ),
210 + );
211 Object.defineProperty(
212 this,
213 'preventDefault',
@@ -237,49 +248,6 @@ SyntheticEvent.extend = function(Interface) {
248 return Class;
249 };
250
240 -/** Proxying after everything set on SyntheticEvent
241 - * to resolve Proxy issue on some WebKit browsers
242 - * in which some Event properties are set to undefined (GH#10010)
243 - */
244 -if (__DEV__) {
245 - const isProxySupported =
246 - typeof Proxy === 'function' &&
247 - // https://github.com/facebook/react/issues/12011
248 - !Object.isSealed(new Proxy({}, {}));
249 -
250 - if (isProxySupported) {
251 - /*eslint-disable no-func-assign */
252 - SyntheticEvent = new Proxy(SyntheticEvent, {
253 - construct: function(target, args) {
254 - return this.apply(target, Object.create(target.prototype), args);
255 - },
256 - apply: function(constructor, that, args) {
257 - return new Proxy(constructor.apply(that, args), {
258 - set: function(target, prop, value) {
259 - if (
260 - prop !== 'isPersistent' &&
261 - !target.constructor.Interface.hasOwnProperty(prop) &&
262 - shouldBeReleasedProperties.indexOf(prop) === -1
263 - ) {
264 - warningWithoutStack(
265 - didWarnForAddedNewProperty || target.isPersistent(),
266 - "This synthetic event is reused for performance reasons. If you're " +
267 - "seeing this, you're adding a new property in the synthetic event object. " +
268 - 'The property is never released. See ' +
269 - 'https://fb.me/react-event-pooling for more information.',
270 - );
271 - didWarnForAddedNewProperty = true;
272 - }
273 - target[prop] = value;
274 - return true;
275 - },
276 - });
277 - },
278 - });
279 - /*eslint-enable no-func-assign */
280 - }
281 -}
282 -
251 addEventPoolingTo(SyntheticEvent);
252
253 /**
@@ -354,7 +322,7 @@ function releasePooledEvent(event) {
322 const EventConstructor = this;
323 invariant(
324 event instanceof EventConstructor,
357 - 'Trying to release an event instance into a pool of a different type.',
325 + 'Trying to release an event instance into a pool of a different type.',
326 );
327 event.destructor();
328 if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
packages/react-dom/src/events/__tests__/SyntheticEvent-test.js
+69 -13
@@ -249,6 +249,62 @@ describe('SyntheticEvent', () => {
249 expect(expectedCount).toBe(1);
250 });
251
252 + it('should warn when calling `isPropagationStopped` if the synthetic event has not been persisted', () => {
253 + let node;
254 + let expectedCount = 0;
255 + let syntheticEvent;
256 +
257 + const eventHandler = e => {
258 + syntheticEvent = e;
259 + expectedCount++;
260 + };
261 + node = ReactDOM.render(<div onClick={eventHandler} />, container);
262 +
263 + const event = document.createEvent('Event');
264 + event.initEvent('click', true, true);
265 + node.dispatchEvent(event);
266 +
267 + expect(() =>
268 + expect(syntheticEvent.isPropagationStopped()).toBe(false),
269 + ).toWarnDev(
270 + 'Warning: This synthetic event is reused for performance reasons. If ' +
271 + "you're seeing this, you're accessing the method `isPropagationStopped` on a " +
272 + 'released/nullified synthetic event. This is a no-op function. If you must ' +
273 + 'keep the original synthetic event around, use event.persist(). ' +
274 + 'See https://fb.me/react-event-pooling for more information.',
275 + {withoutStack: true},
276 + );
277 + expect(expectedCount).toBe(1);
278 + });
279 +
280 + it('should warn when calling `isDefaultPrevented` if the synthetic event has not been persisted', () => {
281 + let node;
282 + let expectedCount = 0;
283 + let syntheticEvent;
284 +
285 + const eventHandler = e => {
286 + syntheticEvent = e;
287 + expectedCount++;
288 + };
289 + node = ReactDOM.render(<div onClick={eventHandler} />, container);
290 +
291 + const event = document.createEvent('Event');
292 + event.initEvent('click', true, true);
293 + node.dispatchEvent(event);
294 +
295 + expect(() =>
296 + expect(syntheticEvent.isDefaultPrevented()).toBe(false),
297 + ).toWarnDev(
298 + 'Warning: This synthetic event is reused for performance reasons. If ' +
299 + "you're seeing this, you're accessing the method `isDefaultPrevented` on a " +
300 + 'released/nullified synthetic event. This is a no-op function. If you must ' +
301 + 'keep the original synthetic event around, use event.persist(). ' +
302 + 'See https://fb.me/react-event-pooling for more information.',
303 + {withoutStack: true},
304 + );
305 + expect(expectedCount).toBe(1);
306 + });
307 +
308 it('should properly log warnings when events simulated with rendered components', () => {
309 let event;
310 function assignEvent(e) {
@@ -270,25 +326,25 @@ describe('SyntheticEvent', () => {
326 );
327 });
328
273 - it('should warn if Proxy is supported and the synthetic event is added a property', () => {
329 + // TODO: we might want to re-add a warning like this in the future,
330 + // but it shouldn't use Proxies because they make debugging difficult.
331 + // Or we might disallow this pattern altogether:
332 + // https://github.com/facebook/react/issues/13224
333 + xit('should warn if a property is added to the synthetic event', () => {
334 let node;
335 let expectedCount = 0;
336 let syntheticEvent;
337
338 const eventHandler = e => {
279 - if (typeof Proxy === 'function') {
280 - expect(() => {
281 - e.foo = 'bar';
282 - }).toWarnDev(
283 - 'Warning: This synthetic event is reused for performance reasons. If ' +
284 - "you're seeing this, you're adding a new property in the synthetic " +
285 - 'event object. The property is never released. ' +
286 - 'See https://fb.me/react-event-pooling for more information.',
287 - {withoutStack: true},
288 - );
289 - } else {
339 + expect(() => {
340 e.foo = 'bar';
291 - }
341 + }).toWarnDev(
342 + 'Warning: This synthetic event is reused for performance reasons. If ' +
343 + "you're seeing this, you're adding a new property in the synthetic " +
344 + 'event object. The property is never released. ' +
345 + 'See https://fb.me/react-event-pooling for more information.',
346 + {withoutStack: true},
347 + );
348 syntheticEvent = e;
349 expectedCount++;
350 };