Remove event pooling in the modern system (#18969)

This commit is contained in:
Dominic Gannaway 2020-05-21 13:54:05 +01:00 committed by GitHub
parent 22f7663f14
commit 4a3f779d67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 423 additions and 372 deletions

View File

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

View File

@ -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();

View File

@ -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();

View File

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

View File

@ -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.

View File

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

View File

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