Remove event pooling in the modern system (#18969)
This commit is contained in:
parent
22f7663f14
commit
4a3f779d67
|
@ -157,7 +157,10 @@ Object.assign(SyntheticEvent.prototype, {
|
|||
* won't be added back into the pool.
|
||||
*/
|
||||
persist: function() {
|
||||
this.isPersistent = functionThatReturnsTrue;
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
this.isPersistent = functionThatReturnsTrue;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -165,65 +168,68 @@ Object.assign(SyntheticEvent.prototype, {
|
|||
*
|
||||
* @return {boolean} True if this should not be released, false otherwise.
|
||||
*/
|
||||
isPersistent: functionThatReturnsFalse,
|
||||
isPersistent: enableModernEventSystem
|
||||
? functionThatReturnsTrue
|
||||
: functionThatReturnsFalse,
|
||||
|
||||
/**
|
||||
* `PooledClass` looks for `destructor` on each instance it releases.
|
||||
*/
|
||||
destructor: function() {
|
||||
const Interface = this.constructor.Interface;
|
||||
for (const propName in Interface) {
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
const Interface = this.constructor.Interface;
|
||||
for (const propName in Interface) {
|
||||
if (__DEV__) {
|
||||
Object.defineProperty(
|
||||
this,
|
||||
propName,
|
||||
getPooledWarningPropertyDefinition(propName, Interface[propName]),
|
||||
);
|
||||
} else {
|
||||
this[propName] = null;
|
||||
}
|
||||
}
|
||||
this.dispatchConfig = null;
|
||||
this._targetInst = null;
|
||||
this.nativeEvent = null;
|
||||
this.isDefaultPrevented = functionThatReturnsFalse;
|
||||
this.isPropagationStopped = functionThatReturnsFalse;
|
||||
this._dispatchListeners = null;
|
||||
this._dispatchInstances = null;
|
||||
if (__DEV__) {
|
||||
Object.defineProperty(
|
||||
this,
|
||||
propName,
|
||||
getPooledWarningPropertyDefinition(propName, Interface[propName]),
|
||||
'nativeEvent',
|
||||
getPooledWarningPropertyDefinition('nativeEvent', null),
|
||||
);
|
||||
} else {
|
||||
this[propName] = null;
|
||||
}
|
||||
}
|
||||
this.dispatchConfig = null;
|
||||
this._targetInst = null;
|
||||
this.nativeEvent = null;
|
||||
this.isDefaultPrevented = functionThatReturnsFalse;
|
||||
this.isPropagationStopped = functionThatReturnsFalse;
|
||||
if (!enableModernEventSystem) {
|
||||
this._dispatchListeners = null;
|
||||
this._dispatchInstances = null;
|
||||
}
|
||||
if (__DEV__) {
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'nativeEvent',
|
||||
getPooledWarningPropertyDefinition('nativeEvent', null),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'isDefaultPrevented',
|
||||
getPooledWarningPropertyDefinition(
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'isDefaultPrevented',
|
||||
functionThatReturnsFalse,
|
||||
),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'isPropagationStopped',
|
||||
getPooledWarningPropertyDefinition(
|
||||
getPooledWarningPropertyDefinition(
|
||||
'isDefaultPrevented',
|
||||
functionThatReturnsFalse,
|
||||
),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'isPropagationStopped',
|
||||
functionThatReturnsFalse,
|
||||
),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'preventDefault',
|
||||
getPooledWarningPropertyDefinition('preventDefault', () => {}),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'stopPropagation',
|
||||
getPooledWarningPropertyDefinition('stopPropagation', () => {}),
|
||||
);
|
||||
getPooledWarningPropertyDefinition(
|
||||
'isPropagationStopped',
|
||||
functionThatReturnsFalse,
|
||||
),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'preventDefault',
|
||||
getPooledWarningPropertyDefinition('preventDefault', () => {}),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'stopPropagation',
|
||||
getPooledWarningPropertyDefinition('stopPropagation', () => {}),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -303,18 +309,26 @@ function getPooledWarningPropertyDefinition(propName, getVal) {
|
|||
}
|
||||
}
|
||||
|
||||
function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
|
||||
function createOrGetPooledEvent(
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeInst,
|
||||
) {
|
||||
const EventConstructor = this;
|
||||
if (EventConstructor.eventPool.length) {
|
||||
const instance = EventConstructor.eventPool.pop();
|
||||
EventConstructor.call(
|
||||
instance,
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeInst,
|
||||
);
|
||||
return instance;
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
if (EventConstructor.eventPool.length) {
|
||||
const instance = EventConstructor.eventPool.pop();
|
||||
EventConstructor.call(
|
||||
instance,
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeInst,
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
return new EventConstructor(
|
||||
dispatchConfig,
|
||||
|
@ -325,21 +339,28 @@ function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
|
|||
}
|
||||
|
||||
function releasePooledEvent(event) {
|
||||
const EventConstructor = this;
|
||||
invariant(
|
||||
event instanceof EventConstructor,
|
||||
'Trying to release an event instance into a pool of a different type.',
|
||||
);
|
||||
event.destructor();
|
||||
if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
|
||||
EventConstructor.eventPool.push(event);
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
const EventConstructor = this;
|
||||
invariant(
|
||||
event instanceof EventConstructor,
|
||||
'Trying to release an event instance into a pool of a different type.',
|
||||
);
|
||||
event.destructor();
|
||||
if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
|
||||
EventConstructor.eventPool.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addEventPoolingTo(EventConstructor) {
|
||||
EventConstructor.eventPool = [];
|
||||
EventConstructor.getPooled = getPooledEvent;
|
||||
EventConstructor.release = releasePooledEvent;
|
||||
EventConstructor.getPooled = createOrGetPooledEvent;
|
||||
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
EventConstructor.eventPool = [];
|
||||
EventConstructor.release = releasePooledEvent;
|
||||
}
|
||||
}
|
||||
|
||||
export default SyntheticEvent;
|
||||
|
|
|
@ -207,10 +207,7 @@ export function dispatchEventsInBatch(dispatchQueue: DispatchQueue): void {
|
|||
const dispatchQueueItem: DispatchQueueItem = dispatchQueue[i];
|
||||
const {event, capture, bubble} = dispatchQueueItem;
|
||||
executeDispatchesInOrder(event, capture, bubble);
|
||||
// Release the event from the pool if needed
|
||||
if (!event.isPersistent()) {
|
||||
event.constructor.release(event);
|
||||
}
|
||||
// Modern event system doesn't use pooling.
|
||||
}
|
||||
// This would be a good time to rethrow if any of the event handlers threw.
|
||||
rethrowCaughtError();
|
||||
|
|
|
@ -78,6 +78,27 @@ describe('DOMModernPluginEventSystem', () => {
|
|||
endNativeEventListenerClearDown();
|
||||
});
|
||||
|
||||
it('does not pool events', () => {
|
||||
const buttonRef = React.createRef();
|
||||
const log = [];
|
||||
const onClick = jest.fn(e => log.push(e));
|
||||
|
||||
function Test() {
|
||||
return <button ref={buttonRef} onClick={onClick} />;
|
||||
}
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
|
||||
let buttonElement = buttonRef.current;
|
||||
dispatchClickEvent(buttonElement);
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
dispatchClickEvent(buttonElement);
|
||||
expect(onClick).toHaveBeenCalledTimes(2);
|
||||
expect(log[0]).not.toBe(log[1]);
|
||||
expect(log[0].type).toBe('click');
|
||||
expect(log[1].type).toBe('click');
|
||||
});
|
||||
|
||||
it('handle propagation of click events', () => {
|
||||
const buttonRef = React.createRef();
|
||||
const divRef = React.createRef();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
describe('SyntheticClipboardEvent', () => {
|
||||
let container;
|
||||
|
@ -117,41 +118,43 @@ describe('SyntheticClipboardEvent', () => {
|
|||
expect(expectedCount).toBe(3);
|
||||
});
|
||||
|
||||
it('is able to `persist`', () => {
|
||||
const persistentEvents = [];
|
||||
const eventHandler = event => {
|
||||
expect(event.isPersistent()).toBe(false);
|
||||
event.persist();
|
||||
expect(event.isPersistent()).toBe(true);
|
||||
persistentEvents.push(event);
|
||||
};
|
||||
if (!ReactFeatureFlags.enableModernEventSystem) {
|
||||
it('is able to `persist`', () => {
|
||||
const persistentEvents = [];
|
||||
const eventHandler = event => {
|
||||
expect(event.isPersistent()).toBe(false);
|
||||
event.persist();
|
||||
expect(event.isPersistent()).toBe(true);
|
||||
persistentEvents.push(event);
|
||||
};
|
||||
|
||||
const div = ReactDOM.render(
|
||||
<div
|
||||
onCopy={eventHandler}
|
||||
onCut={eventHandler}
|
||||
onPaste={eventHandler}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
const div = ReactDOM.render(
|
||||
<div
|
||||
onCopy={eventHandler}
|
||||
onCut={eventHandler}
|
||||
onPaste={eventHandler}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
|
||||
let event;
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent('copy', true, true);
|
||||
div.dispatchEvent(event);
|
||||
let event;
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent('copy', true, true);
|
||||
div.dispatchEvent(event);
|
||||
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent('cut', true, true);
|
||||
div.dispatchEvent(event);
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent('cut', true, true);
|
||||
div.dispatchEvent(event);
|
||||
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent('paste', true, true);
|
||||
div.dispatchEvent(event);
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent('paste', true, true);
|
||||
div.dispatchEvent(event);
|
||||
|
||||
expect(persistentEvents.length).toBe(3);
|
||||
expect(persistentEvents[0].type).toBe('copy');
|
||||
expect(persistentEvents[1].type).toBe('cut');
|
||||
expect(persistentEvents[2].type).toBe('paste');
|
||||
});
|
||||
expect(persistentEvents.length).toBe(3);
|
||||
expect(persistentEvents[0].type).toBe('copy');
|
||||
expect(persistentEvents[1].type).toBe('cut');
|
||||
expect(persistentEvents[2].type).toBe('paste');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
describe('SyntheticEvent', () => {
|
||||
let container;
|
||||
|
@ -99,223 +100,225 @@ describe('SyntheticEvent', () => {
|
|||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should be able to `persist`', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
if (!ReactFeatureFlags.enableModernEventSystem) {
|
||||
it('should be able to `persist`', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
expect(e.isPersistent()).toBe(false);
|
||||
e.persist();
|
||||
syntheticEvent = e;
|
||||
expect(e.isPersistent()).toBe(true);
|
||||
const eventHandler = e => {
|
||||
expect(e.isPersistent()).toBe(false);
|
||||
e.persist();
|
||||
syntheticEvent = e;
|
||||
expect(e.isPersistent()).toBe(true);
|
||||
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(syntheticEvent.type).toBe('click');
|
||||
expect(syntheticEvent.bubbles).toBe(true);
|
||||
expect(syntheticEvent.cancelable).toBe(true);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
expect(syntheticEvent.type).toBe('click');
|
||||
expect(syntheticEvent.bubbles).toBe(true);
|
||||
expect(syntheticEvent.cancelable).toBe(true);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should be nullified and log warnings if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
it('should be nullified and log warnings if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
const getExpectedWarning = property =>
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
`you're seeing this, you're accessing the property \`${property}\` on a ` +
|
||||
'released/nullified synthetic event. This is set to null. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.';
|
||||
|
||||
// once for each property accessed
|
||||
expect(() =>
|
||||
expect(syntheticEvent.type).toBe(null),
|
||||
).toErrorDev(getExpectedWarning('type'), {withoutStack: true});
|
||||
expect(() =>
|
||||
expect(syntheticEvent.nativeEvent).toBe(null),
|
||||
).toErrorDev(getExpectedWarning('nativeEvent'), {withoutStack: true});
|
||||
expect(() =>
|
||||
expect(syntheticEvent.target).toBe(null),
|
||||
).toErrorDev(getExpectedWarning('target'), {withoutStack: true});
|
||||
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when setting properties of a synthetic event that has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() => {
|
||||
syntheticEvent.type = 'MouseEvent';
|
||||
}).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're setting the property `type` on a " +
|
||||
'released/nullified synthetic event. This is effectively a no-op. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `preventDefault` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
syntheticEvent.preventDefault(),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `preventDefault` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `stopPropagation` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
syntheticEvent.stopPropagation(),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `stopPropagation` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `isPropagationStopped` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
expect(syntheticEvent.isPropagationStopped()).toBe(false),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `isPropagationStopped` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `isDefaultPrevented` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
expect(syntheticEvent.isDefaultPrevented()).toBe(false),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `isDefaultPrevented` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should properly log warnings when events simulated with rendered components', () => {
|
||||
let event;
|
||||
function assignEvent(e) {
|
||||
event = e;
|
||||
}
|
||||
const node = ReactDOM.render(<div onClick={assignEvent} />, container);
|
||||
node.click();
|
||||
|
||||
// access a property to cause the warning
|
||||
expect(() => {
|
||||
event.nativeEvent; // eslint-disable-line no-unused-expressions
|
||||
}).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the property `nativeEvent` on a " +
|
||||
const getExpectedWarning = property =>
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
`you're seeing this, you're accessing the property \`${property}\` on a ` +
|
||||
'released/nullified synthetic event. This is set to null. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
'See https://fb.me/react-event-pooling for more information.';
|
||||
|
||||
// once for each property accessed
|
||||
expect(() =>
|
||||
expect(syntheticEvent.type).toBe(null),
|
||||
).toErrorDev(getExpectedWarning('type'), {withoutStack: true});
|
||||
expect(() =>
|
||||
expect(syntheticEvent.nativeEvent).toBe(null),
|
||||
).toErrorDev(getExpectedWarning('nativeEvent'), {withoutStack: true});
|
||||
expect(() =>
|
||||
expect(syntheticEvent.target).toBe(null),
|
||||
).toErrorDev(getExpectedWarning('target'), {withoutStack: true});
|
||||
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when setting properties of a synthetic event that has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() => {
|
||||
syntheticEvent.type = 'MouseEvent';
|
||||
}).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're setting the property `type` on a " +
|
||||
'released/nullified synthetic event. This is effectively a no-op. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `preventDefault` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
syntheticEvent.preventDefault(),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `preventDefault` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `stopPropagation` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
syntheticEvent.stopPropagation(),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `stopPropagation` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `isPropagationStopped` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
expect(syntheticEvent.isPropagationStopped()).toBe(false),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `isPropagationStopped` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn when calling `isDefaultPrevented` if the synthetic event has not been persisted', () => {
|
||||
let expectedCount = 0;
|
||||
let syntheticEvent;
|
||||
|
||||
const eventHandler = e => {
|
||||
syntheticEvent = e;
|
||||
expectedCount++;
|
||||
};
|
||||
const node = ReactDOM.render(<div onClick={eventHandler} />, container);
|
||||
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(() =>
|
||||
expect(syntheticEvent.isDefaultPrevented()).toBe(false),
|
||||
).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the method `isDefaultPrevented` on a " +
|
||||
'released/nullified synthetic event. This is a no-op function. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(expectedCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should properly log warnings when events simulated with rendered components', () => {
|
||||
let event;
|
||||
function assignEvent(e) {
|
||||
event = e;
|
||||
}
|
||||
const node = ReactDOM.render(<div onClick={assignEvent} />, container);
|
||||
node.click();
|
||||
|
||||
// access a property to cause the warning
|
||||
expect(() => {
|
||||
event.nativeEvent; // eslint-disable-line no-unused-expressions
|
||||
}).toErrorDev(
|
||||
'Warning: This synthetic event is reused for performance reasons. If ' +
|
||||
"you're seeing this, you're accessing the property `nativeEvent` on a " +
|
||||
'released/nullified synthetic event. This is set to null. If you must ' +
|
||||
'keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: we might want to re-add a warning like this in the future,
|
||||
// but it shouldn't use Proxies because they make debugging difficult.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
describe('SyntheticKeyboardEvent', () => {
|
||||
let container;
|
||||
|
@ -547,49 +548,51 @@ describe('SyntheticKeyboardEvent', () => {
|
|||
expect(expectedCount).toBe(3);
|
||||
});
|
||||
|
||||
it('is able to `persist`', () => {
|
||||
const persistentEvents = [];
|
||||
const eventHandler = event => {
|
||||
expect(event.isPersistent()).toBe(false);
|
||||
event.persist();
|
||||
expect(event.isPersistent()).toBe(true);
|
||||
persistentEvents.push(event);
|
||||
};
|
||||
const div = ReactDOM.render(
|
||||
<div
|
||||
onKeyDown={eventHandler}
|
||||
onKeyUp={eventHandler}
|
||||
onKeyPress={eventHandler}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
if (!ReactFeatureFlags.enableModernEventSystem) {
|
||||
it('is able to `persist`', () => {
|
||||
const persistentEvents = [];
|
||||
const eventHandler = event => {
|
||||
expect(event.isPersistent()).toBe(false);
|
||||
event.persist();
|
||||
expect(event.isPersistent()).toBe(true);
|
||||
persistentEvents.push(event);
|
||||
};
|
||||
const div = ReactDOM.render(
|
||||
<div
|
||||
onKeyDown={eventHandler}
|
||||
onKeyUp={eventHandler}
|
||||
onKeyPress={eventHandler}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keyup', {
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
charCode: 40,
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
expect(persistentEvents.length).toBe(3);
|
||||
expect(persistentEvents[0].type).toBe('keydown');
|
||||
expect(persistentEvents[1].type).toBe('keyup');
|
||||
expect(persistentEvents[2].type).toBe('keypress');
|
||||
});
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keyup', {
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
charCode: 40,
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
expect(persistentEvents.length).toBe(3);
|
||||
expect(persistentEvents[0].type).toBe('keydown');
|
||||
expect(persistentEvents[1].type).toBe('keyup');
|
||||
expect(persistentEvents[2].type).toBe('keypress');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
describe('SyntheticWheelEvent', () => {
|
||||
let container;
|
||||
|
@ -113,23 +114,25 @@ describe('SyntheticWheelEvent', () => {
|
|||
expect(events.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should be able to `persist`', () => {
|
||||
const events = [];
|
||||
const onWheel = event => {
|
||||
expect(event.isPersistent()).toBe(false);
|
||||
event.persist();
|
||||
expect(event.isPersistent()).toBe(true);
|
||||
events.push(event);
|
||||
};
|
||||
ReactDOM.render(<div onWheel={onWheel} />, container);
|
||||
if (!ReactFeatureFlags.enableModernEventSystem) {
|
||||
it('should be able to `persist`', () => {
|
||||
const events = [];
|
||||
const onWheel = event => {
|
||||
expect(event.isPersistent()).toBe(false);
|
||||
event.persist();
|
||||
expect(event.isPersistent()).toBe(true);
|
||||
events.push(event);
|
||||
};
|
||||
ReactDOM.render(<div onWheel={onWheel} />, container);
|
||||
|
||||
container.firstChild.dispatchEvent(
|
||||
new MouseEvent('wheel', {
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
container.firstChild.dispatchEvent(
|
||||
new MouseEvent('wheel', {
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(events.length).toBe(1);
|
||||
expect(events[0].type).toBe('wheel');
|
||||
});
|
||||
expect(events.length).toBe(1);
|
||||
expect(events[0].type).toBe('wheel');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue