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