diff --git a/fixtures/dom/.gitignore b/fixtures/dom/.gitignore index bffad9e24b6..6d1a04fd600 100644 --- a/fixtures/dom/.gitignore +++ b/fixtures/dom/.gitignore @@ -9,7 +9,9 @@ coverage # production build public/react.development.js +public/react.production.min.js public/react-dom.development.js +public/react-dom.production.min.js public/react-dom-server.browser.development.js # misc diff --git a/fixtures/dom/package.json b/fixtures/dom/package.json index 0f2959684a6..b3516635ffb 100644 --- a/fixtures/dom/package.json +++ b/fixtures/dom/package.json @@ -18,7 +18,7 @@ }, "scripts": { "start": "react-scripts start", - "prestart": "cp ../../build/dist/react.development.js ../../build/dist/react-dom.development.js ../../build/dist/react-dom-server.browser.development.js public/", + "prestart": "cp ../../build/dist/react.development.js ../../build/dist/react-dom.development.js ../../build/dist/react.production.min.js ../../build/dist/react-dom.production.min.js ../../build/dist/react-dom-server.browser.development.js public/", "build": "react-scripts build && cp build/index.html build/200.html", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js index 9671d2c1d9a..4c0e14dc5b9 100644 --- a/fixtures/dom/src/components/Header.js +++ b/fixtures/dom/src/components/Header.js @@ -7,8 +7,9 @@ class Header extends React.Component { super(props, context); const query = parse(window.location.search); const version = query.version || 'local'; + const production = query.production || false; const versions = [version]; - this.state = {version, versions}; + this.state = {version, versions, production}; } componentWillMount() { getVersionTags().then(tags => { @@ -25,6 +26,14 @@ class Header extends React.Component { } window.location.search = stringify(query); } + handleProductionChange(event) { + const query = parse(window.location.search); + query.production = event.target.checked; + if (!query.production) { + delete query.production; + } + window.location.search = stringify(query); + } handleFixtureChange(event) { window.location.pathname = event.target.value; } @@ -82,6 +91,15 @@ class Header extends React.Component { ))} + + diff --git a/fixtures/dom/src/react-loader.js b/fixtures/dom/src/react-loader.js index b3290bff730..07d8ca92a34 100644 --- a/fixtures/dom/src/react-loader.js +++ b/fixtures/dom/src/react-loader.js @@ -43,29 +43,36 @@ export function reactPaths() { let query = parseQuery(window.location.search); let version = query.version || 'local'; + let production = query.production === 'true'; - if (version !== 'local') { + if (version === 'local' && production) { + reactPath = 'react.production.min.js'; + reactDOMPath = 'react-dom.production.min.js'; + } else if (version !== 'local') { const {major, minor, prerelease} = semver(version); const [preReleaseStage] = prerelease; // The file structure was updated in 16. This wasn't the case for alphas. // Load the old module location for anything less than 16 RC if (major >= 16 && !(minor === 0 && preReleaseStage === 'alpha')) { - reactPath = - 'https://unpkg.com/react@' + version + '/umd/react.development.js'; + const suffix = production ? '.production.min.js' : '.development.js'; + reactPath = 'https://unpkg.com/react@' + version + '/umd/react' + suffix; reactDOMPath = - 'https://unpkg.com/react-dom@' + - version + - '/umd/react-dom.development.js'; + 'https://unpkg.com/react-dom@' + version + '/umd/react-dom' + suffix; reactDOMServerPath = 'https://unpkg.com/react-dom@' + version + - '/umd/react-dom-server.browser.development'; + '/umd/react-dom-server.browser' + + suffix; } else { - reactPath = 'https://unpkg.com/react@' + version + '/dist/react.js'; + const suffix = production ? '.min.js' : '.js'; + reactPath = 'https://unpkg.com/react@' + version + '/dist/react' + suffix; reactDOMPath = - 'https://unpkg.com/react-dom@' + version + '/dist/react-dom.js'; + 'https://unpkg.com/react-dom@' + version + '/dist/react-dom' + suffix; reactDOMServerPath = - 'https://unpkg.com/react-dom@' + version + '/dist/react-dom-server.js'; + 'https://unpkg.com/react-dom@' + + version + + '/dist/react-dom-server' + + suffix; } } diff --git a/fixtures/dom/src/style.css b/fixtures/dom/src/style.css index 6d401295c8c..dbe22485e86 100644 --- a/fixtures/dom/src/style.css +++ b/fixtures/dom/src/style.css @@ -126,6 +126,11 @@ textarea { width: 100%; } +.header__label { + font-size: 12px; + color: #ccc; +} + .sr-only { clip: rect(0, 0, 0, 0); height: 0; diff --git a/packages/events/EventPluginHub.js b/packages/events/EventPluginHub.js index faac1af7e74..affaab04ff1 100644 --- a/packages/events/EventPluginHub.js +++ b/packages/events/EventPluginHub.js @@ -33,30 +33,11 @@ import type {TopLevelType} from './TopLevelEventTypes'; */ let eventQueue: ?(Array | ReactSyntheticEvent) = null; -/** - * Dispatches an event and releases it back into the pool, unless persistent. - * - * @param {?object} event Synthetic event to be dispatched. - * @param {boolean} simulated If the event is simulated (changes exn behavior) - * @private - */ -const executeDispatchesAndRelease = function( - event: ReactSyntheticEvent, - simulated: boolean, -) { - if (event) { - executeDispatchesInOrder(event, simulated); - - if (!event.isPersistent()) { - event.constructor.release(event); - } - } -}; -const executeDispatchesAndReleaseSimulated = function(e) { - return executeDispatchesAndRelease(e, true); +const executeDispatchesSimulated = function(e) { + return executeDispatchesInOrder(e, true); }; -const executeDispatchesAndReleaseTopLevel = function(e) { - return executeDispatchesAndRelease(e, false); +const executeDispatchesTopLevel = function(e) { + return executeDispatchesInOrder(e, false); }; function isInteractive(tag) { @@ -208,15 +189,9 @@ export function runEventsInBatch( } if (simulated) { - forEachAccumulated( - processingEventQueue, - executeDispatchesAndReleaseSimulated, - ); + forEachAccumulated(processingEventQueue, executeDispatchesSimulated); } else { - forEachAccumulated( - processingEventQueue, - executeDispatchesAndReleaseTopLevel, - ); + forEachAccumulated(processingEventQueue, executeDispatchesTopLevel); } invariant( !eventQueue, diff --git a/packages/events/ResponderEventPlugin.js b/packages/events/ResponderEventPlugin.js index 4db54cae877..4ac71d5e756 100644 --- a/packages/events/ResponderEventPlugin.js +++ b/packages/events/ResponderEventPlugin.js @@ -365,7 +365,7 @@ function setResponderAndExtractTransfer( // It's strange to get an `onMoveShouldSetResponder` when you're *already* // the responder. const skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderInst; - const shouldSetEvent = ResponderSyntheticEvent.getPooled( + const shouldSetEvent = new ResponderSyntheticEvent( shouldSetEventType, bubbleShouldSetFrom, nativeEvent, @@ -378,15 +378,12 @@ function setResponderAndExtractTransfer( accumulateTwoPhaseDispatches(shouldSetEvent); } const wantsResponderInst = executeDispatchesInOrderStopAtTrue(shouldSetEvent); - if (!shouldSetEvent.isPersistent()) { - shouldSetEvent.constructor.release(shouldSetEvent); - } if (!wantsResponderInst || wantsResponderInst === responderInst) { return null; } let extracted; - const grantEvent = ResponderSyntheticEvent.getPooled( + const grantEvent = new ResponderSyntheticEvent( eventTypes.responderGrant, wantsResponderInst, nativeEvent, @@ -397,7 +394,7 @@ function setResponderAndExtractTransfer( accumulateDirectDispatches(grantEvent); const blockHostResponder = executeDirectDispatch(grantEvent) === true; if (responderInst) { - const terminationRequestEvent = ResponderSyntheticEvent.getPooled( + const terminationRequestEvent = new ResponderSyntheticEvent( eventTypes.responderTerminationRequest, responderInst, nativeEvent, @@ -409,12 +406,9 @@ function setResponderAndExtractTransfer( const shouldSwitch = !hasDispatches(terminationRequestEvent) || executeDirectDispatch(terminationRequestEvent); - if (!terminationRequestEvent.isPersistent()) { - terminationRequestEvent.constructor.release(terminationRequestEvent); - } if (shouldSwitch) { - const terminateEvent = ResponderSyntheticEvent.getPooled( + const terminateEvent = new ResponderSyntheticEvent( eventTypes.responderTerminate, responderInst, nativeEvent, @@ -425,7 +419,7 @@ function setResponderAndExtractTransfer( extracted = accumulate(extracted, [grantEvent, terminateEvent]); changeResponder(wantsResponderInst, blockHostResponder); } else { - const rejectEvent = ResponderSyntheticEvent.getPooled( + const rejectEvent = new ResponderSyntheticEvent( eventTypes.responderReject, wantsResponderInst, nativeEvent, @@ -553,7 +547,7 @@ const ResponderEventPlugin = { : null; if (incrementalTouch) { - const gesture = ResponderSyntheticEvent.getPooled( + const gesture = new ResponderSyntheticEvent( incrementalTouch, responderInst, nativeEvent, @@ -577,7 +571,7 @@ const ResponderEventPlugin = { ? eventTypes.responderRelease : null; if (finalTouch) { - const finalEvent = ResponderSyntheticEvent.getPooled( + const finalEvent = new ResponderSyntheticEvent( finalTouch, responderInst, nativeEvent, diff --git a/packages/events/SyntheticEvent.js b/packages/events/SyntheticEvent.js index 78812c15db5..6f9abd1741d 100644 --- a/packages/events/SyntheticEvent.js +++ b/packages/events/SyntheticEvent.js @@ -7,11 +7,8 @@ /* eslint valid-typeof: 0 */ -import invariant from 'shared/invariant'; import warningWithoutStack from 'shared/warningWithoutStack'; -const EVENT_POOL_SIZE = 10; - /** * @interface Event * @see http://www.w3.org/TR/DOM-Level-3-Events/ @@ -45,10 +42,10 @@ function functionThatReturnsFalse() { * Synthetic events are dispatched by event plugins, typically in response to a * top-level event delegation handler. * - * These systems should generally use pooling to reduce the frequency of garbage - * collection. The system should check `isPersistent` to determine whether the - * event should be released into the pool after being dispatched. Users that - * need a persisted event should invoke `persist`. + * Events used to use pooling to reduce the frequency of garbage collection, + * but with modern JS engines, this optimizations does not seem necessary + * anymore (and only to the bundle size and complexity of the code). + * We keep the methods `persist` and `isPersistent` for backwards-compatibility. * * Synthetic events (and subclasses) implement the DOM Level 3 Events API by * normalizing browser quirks. Subclasses do not necessarily have to implement a @@ -148,78 +145,25 @@ Object.assign(SyntheticEvent.prototype, { }, /** - * We release all dispatched `SyntheticEvent`s after each event loop, adding - * them back into the pool. This allows a way to hold onto a reference that - * won't be added back into the pool. + * This method was used to hold onto a reference so it won't be added back + * into the pool of events. Since we're not relying on pooling anymore, + * this is a no-op that only issues a warning. */ persist: function() { - this.isPersistent = functionThatReturnsTrue; + warningWithoutStack( + false, + 'Deprecated SyntheticEvent.persist called for %s event', + this.type, + ); }, /** - * Checks if this event should be released back into the pool. + * Used to check if this event should be released back into the pool. + * Now that we're not relying on pooling anymore, this always returns true. * - * @return {boolean} True if this should not be released, false otherwise. + * @return {boolean} True. */ - isPersistent: functionThatReturnsFalse, - - /** - * `PooledClass` looks for `destructor` on each instance it releases. - */ - destructor: function() { - 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, - 'nativeEvent', - getPooledWarningPropertyDefinition('nativeEvent', null), - ); - Object.defineProperty( - this, - 'isDefaultPrevented', - getPooledWarningPropertyDefinition( - 'isDefaultPrevented', - functionThatReturnsFalse, - ), - ); - Object.defineProperty( - this, - 'isPropagationStopped', - getPooledWarningPropertyDefinition( - 'isPropagationStopped', - functionThatReturnsFalse, - ), - ); - Object.defineProperty( - this, - 'preventDefault', - getPooledWarningPropertyDefinition('preventDefault', () => {}), - ); - Object.defineProperty( - this, - 'stopPropagation', - getPooledWarningPropertyDefinition('stopPropagation', () => {}), - ); - } - }, + isPersistent: functionThatReturnsTrue, }); SyntheticEvent.Interface = EventInterface; @@ -243,97 +187,8 @@ SyntheticEvent.extend = function(Interface) { Class.Interface = Object.assign({}, Super.Interface, Interface); Class.extend = Super.extend; - addEventPoolingTo(Class); return Class; }; -addEventPoolingTo(SyntheticEvent); - -/** - * Helper to nullify syntheticEvent instance properties when destructing - * - * @param {String} propName - * @param {?object} getVal - * @return {object} defineProperty object - */ -function getPooledWarningPropertyDefinition(propName, getVal) { - const isFunction = typeof getVal === 'function'; - return { - configurable: true, - set: set, - get: get, - }; - - function set(val) { - const action = isFunction ? 'setting the method' : 'setting the property'; - warn(action, 'This is effectively a no-op'); - return val; - } - - function get() { - const action = isFunction - ? 'accessing the method' - : 'accessing the property'; - const result = isFunction - ? 'This is a no-op function' - : 'This is set to null'; - warn(action, result); - return getVal; - } - - function warn(action, result) { - const warningCondition = false; - warningWithoutStack( - warningCondition, - "This synthetic event is reused for performance reasons. If you're seeing this, " + - "you're %s `%s` on a released/nullified synthetic event. %s. " + - 'If you must keep the original synthetic event around, use event.persist(). ' + - 'See https://fb.me/react-event-pooling for more information.', - action, - propName, - result, - ); - } -} - -function getPooledEvent(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; - } - return new EventConstructor( - 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); - } -} - -function addEventPoolingTo(EventConstructor) { - EventConstructor.eventPool = []; - EventConstructor.getPooled = getPooledEvent; - EventConstructor.release = releasePooledEvent; -} - export default SyntheticEvent; diff --git a/packages/react-dom/src/__tests__/ReactTestUtils-test.js b/packages/react-dom/src/__tests__/ReactTestUtils-test.js index eb6534f36a2..73605f0d683 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtils-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtils-test.js @@ -340,9 +340,7 @@ describe('ReactTestUtils', () => { describe('Simulate', () => { it('should change the value of an input field', () => { const obj = { - handler: function(e) { - e.persist(); - }, + handler: function() {}, }; spyOnDevAndProd(obj, 'handler').and.callThrough(); const container = document.createElement('div'); @@ -375,9 +373,7 @@ describe('ReactTestUtils', () => { } const obj = { - handler: function(e) { - e.persist(); - }, + handler: function() {}, }; spyOnDevAndProd(obj, 'handler').and.callThrough(); const container = document.createElement('div'); @@ -460,7 +456,6 @@ describe('ReactTestUtils', () => { it('should set the type of the event', () => { let event; const stub = jest.fn().mockImplementation(e => { - e.persist(); event = e; }); diff --git a/packages/react-dom/src/events/BeforeInputEventPlugin.js b/packages/react-dom/src/events/BeforeInputEventPlugin.js index dbe8ea3bdad..c3e0378a7d7 100644 --- a/packages/react-dom/src/events/BeforeInputEventPlugin.js +++ b/packages/react-dom/src/events/BeforeInputEventPlugin.js @@ -254,7 +254,7 @@ function extractCompositionEvent( } } - const event = SyntheticCompositionEvent.getPooled( + const event = new SyntheticCompositionEvent( eventType, targetInst, nativeEvent, @@ -425,7 +425,7 @@ function extractBeforeInputEvent( return null; } - const event = SyntheticInputEvent.getPooled( + const event = new SyntheticInputEvent( eventTypes.beforeInput, targetInst, nativeEvent, diff --git a/packages/react-dom/src/events/ChangeEventPlugin.js b/packages/react-dom/src/events/ChangeEventPlugin.js index 1e2f129b5bf..be1bfa0da74 100644 --- a/packages/react-dom/src/events/ChangeEventPlugin.js +++ b/packages/react-dom/src/events/ChangeEventPlugin.js @@ -49,7 +49,7 @@ const eventTypes = { }; function createAndAccumulateChangeEvent(inst, nativeEvent, target) { - const event = SyntheticEvent.getPooled( + const event = new SyntheticEvent( eventTypes.change, inst, nativeEvent, diff --git a/packages/react-dom/src/events/EnterLeaveEventPlugin.js b/packages/react-dom/src/events/EnterLeaveEventPlugin.js index b608da3870d..ce37fb3a656 100644 --- a/packages/react-dom/src/events/EnterLeaveEventPlugin.js +++ b/packages/react-dom/src/events/EnterLeaveEventPlugin.js @@ -120,7 +120,7 @@ const EnterLeaveEventPlugin = { const fromNode = from == null ? win : getNodeFromInstance(from); const toNode = to == null ? win : getNodeFromInstance(to); - const leave = eventInterface.getPooled( + const leave = new eventInterface( leaveEventType, from, nativeEvent, @@ -130,7 +130,7 @@ const EnterLeaveEventPlugin = { leave.target = fromNode; leave.relatedTarget = toNode; - const enter = eventInterface.getPooled( + const enter = new eventInterface( enterEventType, to, nativeEvent, diff --git a/packages/react-dom/src/events/SelectEventPlugin.js b/packages/react-dom/src/events/SelectEventPlugin.js index 926c51ba3e0..9ce9faa64f6 100644 --- a/packages/react-dom/src/events/SelectEventPlugin.js +++ b/packages/react-dom/src/events/SelectEventPlugin.js @@ -128,7 +128,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) { if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) { lastSelection = currentSelection; - const syntheticEvent = SyntheticEvent.getPooled( + const syntheticEvent = new SyntheticEvent( eventTypes.select, activeElementInst, nativeEvent, diff --git a/packages/react-dom/src/events/SimpleEventPlugin.js b/packages/react-dom/src/events/SimpleEventPlugin.js index aa8ffe675a1..c2b2ceeca59 100644 --- a/packages/react-dom/src/events/SimpleEventPlugin.js +++ b/packages/react-dom/src/events/SimpleEventPlugin.js @@ -319,7 +319,7 @@ const SimpleEventPlugin: PluginModule & { EventConstructor = SyntheticEvent; break; } - const event = EventConstructor.getPooled( + const event = new EventConstructor( dispatchConfig, targetInst, nativeEvent, diff --git a/packages/react-dom/src/events/TapEventPlugin.js b/packages/react-dom/src/events/TapEventPlugin.js index c358b0b50f7..21dc46309af 100644 --- a/packages/react-dom/src/events/TapEventPlugin.js +++ b/packages/react-dom/src/events/TapEventPlugin.js @@ -181,7 +181,7 @@ const TapEventPlugin = { let event = null; const distance = getDistance(startCoords, nativeEvent); if (isEndish(topLevelType) && distance < tapMoveThreshold) { - event = SyntheticUIEvent.getPooled( + event = new SyntheticUIEvent( eventTypes.touchTap, targetInst, nativeEvent, diff --git a/packages/react-dom/src/events/__tests__/EnterLeaveEventPlugin-test.js b/packages/react-dom/src/events/__tests__/EnterLeaveEventPlugin-test.js index 33dd3e964ee..89ccb2f7b4d 100644 --- a/packages/react-dom/src/events/__tests__/EnterLeaveEventPlugin-test.js +++ b/packages/react-dom/src/events/__tests__/EnterLeaveEventPlugin-test.js @@ -44,7 +44,6 @@ describe('EnterLeaveEventPlugin', () => { const node = ReactDOM.render(
{ - e.persist(); leaveEvents.push(e); }} />, @@ -77,7 +76,6 @@ describe('EnterLeaveEventPlugin', () => { const node = ReactDOM.render(
{ - e.persist(); enterEvents.push(e); }} />, diff --git a/packages/react-dom/src/events/__tests__/SyntheticClipboardEvent-test.js b/packages/react-dom/src/events/__tests__/SyntheticClipboardEvent-test.js index 6bff9d7f40d..4bc4d570197 100644 --- a/packages/react-dom/src/events/__tests__/SyntheticClipboardEvent-test.js +++ b/packages/react-dom/src/events/__tests__/SyntheticClipboardEvent-test.js @@ -116,42 +116,5 @@ 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); - }; - - const div = ReactDOM.render( -
, - container, - ); - - 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('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'); - }); }); }); diff --git a/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js b/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js index 906ae127cf4..a6575fe2844 100644 --- a/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js +++ b/packages/react-dom/src/events/__tests__/SyntheticEvent-test.js @@ -102,18 +102,18 @@ describe('SyntheticEvent', () => { expect(expectedCount).toBe(1); }); - it('should be able to `persist`', () => { + it('should indicate that it is persisted and warn when calling `persist`', () => { let node; - let expectedCount = 0; let syntheticEvent; const eventHandler = e => { - expect(e.isPersistent()).toBe(false); - e.persist(); + expect(e.isPersistent()).toBe(true); + expect(() => e.persist()).toWarnDev( + 'Deprecated SyntheticEvent.persist called for click event', + {withoutStack: true}, + ); syntheticEvent = e; expect(e.isPersistent()).toBe(true); - - expectedCount++; }; node = ReactDOM.render(
, container); @@ -122,208 +122,6 @@ describe('SyntheticEvent', () => { node.dispatchEvent(event); 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 node; - let expectedCount = 0; - let syntheticEvent; - - const eventHandler = e => { - syntheticEvent = e; - - expectedCount++; - }; - node = ReactDOM.render(
, container); - - 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)).toWarnDev( - getExpectedWarning('type'), - {withoutStack: true}, - ); - expect(() => expect(syntheticEvent.nativeEvent).toBe(null)).toWarnDev( - getExpectedWarning('nativeEvent'), - {withoutStack: true}, - ); - expect(() => expect(syntheticEvent.target).toBe(null)).toWarnDev( - getExpectedWarning('target'), - {withoutStack: true}, - ); - - expect(expectedCount).toBe(1); - }); - - it('should warn when setting properties of a synthetic event that has not been persisted', () => { - let node; - let expectedCount = 0; - let syntheticEvent; - - const eventHandler = e => { - syntheticEvent = e; - - expectedCount++; - }; - node = ReactDOM.render(
, container); - - const event = document.createEvent('Event'); - event.initEvent('click', true, true); - node.dispatchEvent(event); - - expect(() => { - syntheticEvent.type = 'MouseEvent'; - }).toWarnDev( - '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 node; - let expectedCount = 0; - let syntheticEvent; - - const eventHandler = e => { - syntheticEvent = e; - expectedCount++; - }; - node = ReactDOM.render(
, container); - - const event = document.createEvent('Event'); - event.initEvent('click', true, true); - node.dispatchEvent(event); - - expect(() => syntheticEvent.preventDefault()).toWarnDev( - '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 node; - let expectedCount = 0; - let syntheticEvent; - - const eventHandler = e => { - syntheticEvent = e; - expectedCount++; - }; - node = ReactDOM.render(
, container); - - const event = document.createEvent('Event'); - event.initEvent('click', true, true); - - node.dispatchEvent(event); - - expect(() => syntheticEvent.stopPropagation()).toWarnDev( - '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 node; - let expectedCount = 0; - let syntheticEvent; - - const eventHandler = e => { - syntheticEvent = e; - expectedCount++; - }; - node = ReactDOM.render(
, container); - - const event = document.createEvent('Event'); - event.initEvent('click', true, true); - node.dispatchEvent(event); - - expect(() => - expect(syntheticEvent.isPropagationStopped()).toBe(false), - ).toWarnDev( - '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 node; - let expectedCount = 0; - let syntheticEvent; - - const eventHandler = e => { - syntheticEvent = e; - expectedCount++; - }; - node = ReactDOM.render(
, container); - - const event = document.createEvent('Event'); - event.initEvent('click', true, true); - node.dispatchEvent(event); - - expect(() => - expect(syntheticEvent.isDefaultPrevented()).toBe(false), - ).toWarnDev( - '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(
, container); - node.click(); - - // access a property to cause the warning - expect(() => { - event.nativeEvent; // eslint-disable-line no-unused-expressions - }).toWarnDev( - '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, diff --git a/packages/react-dom/src/events/__tests__/SyntheticKeyboardEvent-test.js b/packages/react-dom/src/events/__tests__/SyntheticKeyboardEvent-test.js index 06673f0fbe0..8af4eb02553 100644 --- a/packages/react-dom/src/events/__tests__/SyntheticKeyboardEvent-test.js +++ b/packages/react-dom/src/events/__tests__/SyntheticKeyboardEvent-test.js @@ -499,50 +499,5 @@ 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); - }; - let div = ReactDOM.render( -
, - 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'); - }); }); }); diff --git a/packages/react-dom/src/events/__tests__/SyntheticWheelEvent-test.js b/packages/react-dom/src/events/__tests__/SyntheticWheelEvent-test.js index 924a7f37628..4892d27fcd9 100644 --- a/packages/react-dom/src/events/__tests__/SyntheticWheelEvent-test.js +++ b/packages/react-dom/src/events/__tests__/SyntheticWheelEvent-test.js @@ -32,7 +32,6 @@ describe('SyntheticWheelEvent', () => { it('should normalize properties from the MouseEvent interface', () => { const events = []; const onWheel = event => { - event.persist(); events.push(event); }; ReactDOM.render(
, container); @@ -51,7 +50,6 @@ describe('SyntheticWheelEvent', () => { it('should normalize properties from the WheelEvent interface', () => { const events = []; const onWheel = event => { - event.persist(); events.push(event); }; ReactDOM.render(
, container); @@ -89,7 +87,6 @@ describe('SyntheticWheelEvent', () => { expect(event.isDefaultPrevented()).toBe(false); event.preventDefault(); expect(event.isDefaultPrevented()).toBe(true); - event.persist(); events.push(event); }; ReactDOM.render(
, container); @@ -112,24 +109,4 @@ 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(
, container); - - container.firstChild.dispatchEvent( - new MouseEvent('wheel', { - bubbles: true, - }), - ); - - expect(events.length).toBe(1); - expect(events[0].type).toBe('wheel'); - }); }); diff --git a/packages/react-dom/src/test-utils/ReactTestUtils.js b/packages/react-dom/src/test-utils/ReactTestUtils.js index dad3d0f14e2..fa64d392841 100644 --- a/packages/react-dom/src/test-utils/ReactTestUtils.js +++ b/packages/react-dom/src/test-utils/ReactTestUtils.js @@ -424,9 +424,6 @@ function makeSimulator(eventType) { domNode, ); - // Since we aren't using pooling, always persist the event. This will make - // sure it's marked and won't warn when setting additional properties. - event.persist(); Object.assign(event, eventData); if (dispatchConfig.phasedRegistrationNames) { diff --git a/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js b/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js index 127d7b14635..068902ce674 100644 --- a/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js +++ b/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js @@ -46,7 +46,7 @@ const ReactNativeBridgeEventPlugin = { 'Unsupported top level event type "%s" dispatched', topLevelType, ); - const event = SyntheticEvent.getPooled( + const event = new SyntheticEvent( bubbleDispatchConfig || directDispatchConfig, targetInst, nativeEvent,