Skip to content

Commit acbb4f9

Browse files
authored
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
1 parent 171e0b7 commit acbb4f9

File tree

2 files changed

+95
-71
lines changed

2 files changed

+95
-71
lines changed

packages/events/SyntheticEvent.js

+26-58
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,8 @@
1010
import invariant from 'shared/invariant';
1111
import warningWithoutStack from 'shared/warningWithoutStack';
1212

13-
let didWarnForAddedNewProperty = false;
1413
const EVENT_POOL_SIZE = 10;
1514

16-
const shouldBeReleasedProperties = [
17-
'dispatchConfig',
18-
'_targetInst',
19-
'nativeEvent',
20-
'isDefaultPrevented',
21-
'isPropagationStopped',
22-
'_dispatchListeners',
23-
'_dispatchInstances',
24-
];
25-
2615
/**
2716
* @interface Event
2817
* @see http://www.w3.org/TR/DOM-Level-3-Events/
@@ -81,6 +70,8 @@ function SyntheticEvent(
8170
delete this.nativeEvent;
8271
delete this.preventDefault;
8372
delete this.stopPropagation;
73+
delete this.isDefaultPrevented;
74+
delete this.isPropagationStopped;
8475
}
8576

8677
this.dispatchConfig = dispatchConfig;
@@ -188,15 +179,35 @@ Object.assign(SyntheticEvent.prototype, {
188179
this[propName] = null;
189180
}
190181
}
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;
194189
if (__DEV__) {
195190
Object.defineProperty(
196191
this,
197192
'nativeEvent',
198193
getPooledWarningPropertyDefinition('nativeEvent', null),
199194
);
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+
);
200211
Object.defineProperty(
201212
this,
202213
'preventDefault',
@@ -237,49 +248,6 @@ SyntheticEvent.extend = function(Interface) {
237248
return Class;
238249
};
239250

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-
283251
addEventPoolingTo(SyntheticEvent);
284252

285253
/**
@@ -354,7 +322,7 @@ function releasePooledEvent(event) {
354322
const EventConstructor = this;
355323
invariant(
356324
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.',
358326
);
359327
event.destructor();
360328
if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {

packages/react-dom/src/events/__tests__/SyntheticEvent-test.js

+69-13
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,62 @@ describe('SyntheticEvent', () => {
249249
expect(expectedCount).toBe(1);
250250
});
251251

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+
252308
it('should properly log warnings when events simulated with rendered components', () => {
253309
let event;
254310
function assignEvent(e) {
@@ -270,25 +326,25 @@ describe('SyntheticEvent', () => {
270326
);
271327
});
272328

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', () => {
274334
let node;
275335
let expectedCount = 0;
276336
let syntheticEvent;
277337

278338
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(() => {
290340
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+
);
292348
syntheticEvent = e;
293349
expectedCount++;
294350
};

0 commit comments

Comments
 (0)