+
Trace your mouse over this box.
+
+ Last movement: {x},{y}
+
+
+ );
+ }
+}
+
+export default HitBox;
diff --git a/fixtures/dom/src/components/fixtures/event-pooling/index.js b/fixtures/dom/src/components/fixtures/event-pooling/index.js
new file mode 100644
index 00000000000..235a7c60153
--- /dev/null
+++ b/fixtures/dom/src/components/fixtures/event-pooling/index.js
@@ -0,0 +1,18 @@
+import FixtureSet from '../../FixtureSet';
+import MouseMove from './mouse-move';
+import Persistence from './persistence';
+
+const React = window.React;
+
+class EventPooling extends React.Component {
+ render() {
+ return (
+ Please select a test fixture.
;
}
diff --git a/scripts/rollup/results.json b/scripts/rollup/results.json
index cbeb82c8626..1141df423b0 100644
--- a/scripts/rollup/results.json
+++ b/scripts/rollup/results.json
@@ -1,16 +1,16 @@
{
"bundleSizes": {
"react.development.js (UMD_DEV)": {
- "size": 65960,
- "gzip": 16789
+ "size": 65980,
+ "gzip": 16788
},
"react.production.min.js (UMD_PROD)": {
"size": 6734,
"gzip": 2757
},
"react.development.js (NODE_DEV)": {
- "size": 56282,
- "gzip": 14446
+ "size": 56302,
+ "gzip": 14443
},
"react.production.min.js (NODE_PROD)": {
"size": 5596,
@@ -25,76 +25,76 @@
"gzip": 6703
},
"react-dom.development.js (UMD_DEV)": {
- "size": 635877,
- "gzip": 145302
+ "size": 634973,
+ "gzip": 144789
},
"react-dom.production.min.js (UMD_PROD)": {
- "size": 120935,
- "gzip": 38567
+ "size": 119734,
+ "gzip": 38187
},
"react-dom.development.js (NODE_DEV)": {
- "size": 595290,
- "gzip": 135669
+ "size": 594382,
+ "gzip": 135201
},
"react-dom.production.min.js (NODE_PROD)": {
- "size": 117857,
- "gzip": 37500
+ "size": 116650,
+ "gzip": 37146
},
"ReactDOMFiber-dev.js (FB_DEV)": {
- "size": 592102,
- "gzip": 135156
+ "size": 591210,
+ "gzip": 134689
},
"ReactDOMFiber-prod.js (FB_PROD)": {
- "size": 426047,
- "gzip": 95491
+ "size": 424757,
+ "gzip": 95144
},
"react-dom-test-utils.development.js (NODE_DEV)": {
- "size": 55584,
- "gzip": 14051
+ "size": 53568,
+ "gzip": 13486
},
"ReactTestUtils-dev.js (FB_DEV)": {
- "size": 55374,
- "gzip": 14012
+ "size": 53378,
+ "gzip": 13448
},
"react-dom-unstable-native-dependencies.development.js (UMD_DEV)": {
- "size": 90271,
- "gzip": 22897
+ "size": 88255,
+ "gzip": 22338
},
"react-dom-unstable-native-dependencies.production.min.js (UMD_PROD)": {
- "size": 18338,
- "gzip": 5956
+ "size": 18178,
+ "gzip": 5939
},
"react-dom-unstable-native-dependencies.development.js (NODE_DEV)": {
- "size": 83719,
- "gzip": 20982
+ "size": 81703,
+ "gzip": 20376
},
"react-dom-unstable-native-dependencies.production.min.js (NODE_PROD)": {
- "size": 16978,
- "gzip": 5429
+ "size": 16448,
+ "gzip": 5300
},
"ReactDOMUnstableNativeDependencies-dev.js (FB_DEV)": {
- "size": 83418,
- "gzip": 20945
+ "size": 81422,
+ "gzip": 20340
},
"ReactDOMUnstableNativeDependencies-prod.js (FB_PROD)": {
- "size": 67022,
- "gzip": 15921
+ "size": 66066,
+ "gzip": 15736
},
"react-dom-server.browser.development.js (UMD_DEV)": {
- "size": 123103,
- "gzip": 31119
+ "size": 123129,
+ "gzip": 31128
},
"react-dom-server.browser.production.min.js (UMD_PROD)": {
- "size": 22069,
- "gzip": 8359
+ "size": 21353,
+ "gzip": 8162
},
"react-dom-server.browser.development.js (NODE_DEV)": {
- "size": 92259,
+ "size": 92285,
"gzip": 23724
},
"react-dom-server.browser.production.min.js (NODE_PROD)": {
- "size": 20470,
- "gzip": 7751
+ "size": 20116,
+ "gzip": 7657
},
"ReactDOMServer-dev.js (FB_DEV)": {
"size": 91498,
@@ -105,60 +105,60 @@
"gzip": 13944
},
"react-dom-server.node.development.js (NODE_DEV)": {
- "size": 94946,
- "gzip": 24289
+ "size": 95018,
+ "gzip": 24291
},
"react-dom-server.node.production.min.js (NODE_PROD)": {
- "size": 21375,
- "gzip": 8033
+ "size": 21207,
+ "gzip": 8013
},
"react-art.development.js (UMD_DEV)": {
- "size": 372653,
- "gzip": 82680
+ "size": 373741,
+ "gzip": 82785
},
"react-art.production.min.js (UMD_PROD)": {
- "size": 93748,
- "gzip": 29003
+ "size": 93224,
+ "gzip": 28829
},
"react-art.development.js (NODE_DEV)": {
- "size": 294040,
- "gzip": 62650
+ "size": 295128,
+ "gzip": 62771
},
"react-art.production.min.js (NODE_PROD)": {
- "size": 55318,
- "gzip": 17053
+ "size": 55164,
+ "gzip": 16977
},
"ReactARTFiber-dev.js (FB_DEV)": {
- "size": 293027,
- "gzip": 62742
+ "size": 294057,
+ "gzip": 62846
},
"ReactARTFiber-prod.js (FB_PROD)": {
"size": 219827,
"gzip": 45605
},
"ReactNativeStack-dev.js (RN_DEV)": {
- "size": 200977,
- "gzip": 37381
+ "size": 201902,
+ "gzip": 37570
},
"ReactNativeStack-prod.js (RN_PROD)": {
- "size": 137361,
- "gzip": 26398
+ "size": 138226,
+ "gzip": 26573
},
"ReactNativeFiber-dev.js (RN_DEV)": {
- "size": 307751,
- "gzip": 53404
+ "size": 307333,
+ "gzip": 53302
},
"ReactNativeFiber-prod.js (RN_PROD)": {
- "size": 224000,
- "gzip": 38939
+ "size": 222948,
+ "gzip": 38742
},
"react-test-renderer.development.js (NODE_DEV)": {
- "size": 291878,
- "gzip": 61552
+ "size": 292928,
+ "gzip": 61669
},
"ReactTestRendererFiber-dev.js (FB_DEV)": {
- "size": 290826,
- "gzip": 61649
+ "size": 291856,
+ "gzip": 61755
},
"react-test-renderer-shallow.development.js (NODE_DEV)": {
"size": 9812,
@@ -169,8 +169,8 @@
"gzip": 2439
},
"react-noop-renderer.development.js (NODE_DEV)": {
- "size": 285805,
- "gzip": 59966
+ "size": 286835,
+ "gzip": 60082
}
}
}
\ No newline at end of file
diff --git a/src/renderers/dom/shared/ReactDOMEventListener.js b/src/renderers/dom/shared/ReactDOMEventListener.js
index 73954793c79..671588b6817 100644
--- a/src/renderers/dom/shared/ReactDOMEventListener.js
+++ b/src/renderers/dom/shared/ReactDOMEventListener.js
@@ -12,7 +12,6 @@
'use strict';
var EventListener = require('fbjs/lib/EventListener');
-var PooledClass = require('PooledClass');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactFiberTreeReflection = require('ReactFiberTreeReflection');
var ReactGenericBatching = require('ReactGenericBatching');
@@ -22,6 +21,9 @@ var getEventTarget = require('getEventTarget');
var {HostRoot} = ReactTypeOfWork;
+var CALLBACK_BOOKKEEPING_POOL_SIZE = 10;
+var callbackBookkeepingPool = [];
+
/**
* Find the deepest React component completely containing the root of the
* passed-in instance (for use when entire React trees are nested within each
@@ -50,24 +52,31 @@ function findRootContainerNode(inst) {
}
// Used to store ancestor hierarchy in top level callback
-function TopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst) {
- this.topLevelType = topLevelType;
- this.nativeEvent = nativeEvent;
- this.targetInst = targetInst;
- this.ancestors = [];
+function getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst) {
+ if (callbackBookkeepingPool.length) {
+ const instance = callbackBookkeepingPool.pop();
+ instance.topLevelType = topLevelType;
+ instance.nativeEvent = nativeEvent;
+ instance.targetInst = targetInst;
+ return instance;
+ }
+ return {
+ topLevelType,
+ nativeEvent,
+ targetInst,
+ ancestors: [],
+ };
+}
+
+function releaseTopLevelCallbackBookKeeping(instance) {
+ instance.topLevelType = null;
+ instance.nativeEvent = null;
+ instance.targetInst = null;
+ instance.ancestors.length = 0;
+ if (callbackBookkeepingPool.length < CALLBACK_BOOKKEEPING_POOL_SIZE) {
+ callbackBookkeepingPool.push(instance);
+ }
}
-Object.assign(TopLevelCallbackBookKeeping.prototype, {
- destructor: function() {
- this.topLevelType = null;
- this.nativeEvent = null;
- this.targetInst = null;
- this.ancestors.length = 0;
- },
-});
-PooledClass.addPoolingTo(
- TopLevelCallbackBookKeeping,
- PooledClass.threeArgumentPooler,
-);
function handleTopLevelImpl(bookKeeping) {
var targetInst = bookKeeping.targetInst;
@@ -180,7 +189,7 @@ var ReactDOMEventListener = {
targetInst = null;
}
- var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
+ var bookKeeping = getTopLevelCallbackBookKeeping(
topLevelType,
nativeEvent,
targetInst,
@@ -191,7 +200,7 @@ var ReactDOMEventListener = {
// `preventDefault`.
ReactGenericBatching.batchedUpdates(handleTopLevelImpl, bookKeeping);
} finally {
- TopLevelCallbackBookKeeping.release(bookKeeping);
+ releaseTopLevelCallbackBookKeeping(bookKeeping);
}
},
};
diff --git a/src/renderers/dom/shared/eventPlugins/BeforeInputEventPlugin.js b/src/renderers/dom/shared/eventPlugins/BeforeInputEventPlugin.js
index cd566d84002..09575109421 100644
--- a/src/renderers/dom/shared/eventPlugins/BeforeInputEventPlugin.js
+++ b/src/renderers/dom/shared/eventPlugins/BeforeInputEventPlugin.js
@@ -209,8 +209,8 @@ function getDataFromCustomEvent(nativeEvent) {
return null;
}
-// Track the current IME composition fallback object, if any.
-var currentComposition = null;
+// Track the current IME composition status, if any.
+var isComposing = false;
/**
* @return {?object} A SyntheticCompositionEvent.
@@ -226,7 +226,7 @@ function extractCompositionEvent(
if (canUseCompositionEvent) {
eventType = getCompositionEventType(topLevelType);
- } else if (!currentComposition) {
+ } else if (!isComposing) {
if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
eventType = eventTypes.compositionStart;
}
@@ -241,13 +241,11 @@ function extractCompositionEvent(
if (useFallbackCompositionData) {
// The current composition is stored statically and must not be
// overwritten while composition continues.
- if (!currentComposition && eventType === eventTypes.compositionStart) {
- currentComposition = FallbackCompositionState.getPooled(
- nativeEventTarget,
- );
+ if (!isComposing && eventType === eventTypes.compositionStart) {
+ isComposing = FallbackCompositionState.initialize(nativeEventTarget);
} else if (eventType === eventTypes.compositionEnd) {
- if (currentComposition) {
- fallbackData = currentComposition.getData();
+ if (isComposing) {
+ fallbackData = FallbackCompositionState.getData();
}
}
}
@@ -338,15 +336,15 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelTypes, nativeEvent) {
// try to extract the composed characters from the fallback object.
// If composition event is available, we extract a string only at
// compositionevent, otherwise extract it at fallback events.
- if (currentComposition) {
+ if (isComposing) {
if (
topLevelType === 'topCompositionEnd' ||
(!canUseCompositionEvent &&
isFallbackCompositionEnd(topLevelType, nativeEvent))
) {
- var chars = currentComposition.getData();
- FallbackCompositionState.release(currentComposition);
- currentComposition = null;
+ var chars = FallbackCompositionState.getData();
+ FallbackCompositionState.reset();
+ isComposing = false;
return chars;
}
return null;
diff --git a/src/renderers/dom/shared/eventPlugins/FallbackCompositionState.js b/src/renderers/dom/shared/eventPlugins/FallbackCompositionState.js
index e26e259a0f1..41028f88fe4 100644
--- a/src/renderers/dom/shared/eventPlugins/FallbackCompositionState.js
+++ b/src/renderers/dom/shared/eventPlugins/FallbackCompositionState.js
@@ -11,62 +11,46 @@
'use strict';
-var PooledClass = require('PooledClass');
-
var getTextContentAccessor = require('getTextContentAccessor');
/**
- * This helper class stores information about text content of a target node,
+ * This helper object stores information about text content of a target node,
* allowing comparison of content before and after a given event.
*
* Identify the node where selection currently begins, then observe
* both its text content and its current position in the DOM. Since the
* browser may natively replace the target node during composition, we can
* use its position to find its replacement.
+ *
*
- * @param {DOMEventTarget} root
*/
-function FallbackCompositionState(root) {
- this._root = root;
- this._startText = this.getText();
- this._fallbackText = null;
-}
+var compositionState = {
+ _root: null,
+ _startText: null,
+ _fallbackText: null,
+};
-Object.assign(FallbackCompositionState.prototype, {
- destructor: function() {
- this._root = null;
- this._startText = null;
- this._fallbackText = null;
+var FallbackCompositionState = {
+ initialize(nativeEventTarget) {
+ compositionState._root = nativeEventTarget;
+ compositionState._startText = FallbackCompositionState.getText();
+ return true;
},
-
- /**
- * Get current text of input.
- *
- * @return {string}
- */
- getText: function() {
- if ('value' in this._root) {
- return this._root.value;
- }
- return this._root[getTextContentAccessor()];
+ reset() {
+ compositionState._root = null;
+ compositionState._startText = null;
+ compositionState._fallbackText = null;
},
-
- /**
- * Determine the differing substring between the initially stored
- * text content and the current content.
- *
- * @return {string}
- */
- getData: function() {
- if (this._fallbackText) {
- return this._fallbackText;
+ getData() {
+ if (compositionState._fallbackText) {
+ return compositionState._fallbackText;
}
var start;
- var startValue = this._startText;
+ var startValue = compositionState._startText;
var startLength = startValue.length;
var end;
- var endValue = this.getText();
+ var endValue = FallbackCompositionState.getText();
var endLength = endValue.length;
for (start = 0; start < startLength; start++) {
@@ -83,11 +67,15 @@ Object.assign(FallbackCompositionState.prototype, {
}
var sliceTail = end > 1 ? 1 - end : undefined;
- this._fallbackText = endValue.slice(start, sliceTail);
- return this._fallbackText;
+ compositionState._fallbackText = endValue.slice(start, sliceTail);
+ return compositionState._fallbackText;
},
-});
-
-PooledClass.addPoolingTo(FallbackCompositionState);
+ getText() {
+ if ('value' in compositionState._root) {
+ return compositionState._root.value;
+ }
+ return compositionState._root[getTextContentAccessor()];
+ },
+};
module.exports = FallbackCompositionState;
diff --git a/src/renderers/dom/shared/eventPlugins/__tests__/FallbackCompositionState-test.js b/src/renderers/dom/shared/eventPlugins/__tests__/FallbackCompositionState-test.js
index 19af12471c5..a5c5c1e745b 100644
--- a/src/renderers/dom/shared/eventPlugins/__tests__/FallbackCompositionState-test.js
+++ b/src/renderers/dom/shared/eventPlugins/__tests__/FallbackCompositionState-test.js
@@ -43,24 +43,24 @@ describe('FallbackCompositionState', () => {
function assertExtractedData(modifiedValue, expectedData) {
var input = getInput();
- var composition = FallbackCompositionState.getPooled(input);
+ FallbackCompositionState.initialize(input);
input.value = modifiedValue;
- expect(composition.getData()).toBe(expectedData);
- FallbackCompositionState.release(composition);
+ expect(FallbackCompositionState.getData()).toBe(expectedData);
+ FallbackCompositionState.reset();
}
it('extracts value via `getText()`', () => {
- var composition = FallbackCompositionState.getPooled(getInput());
- expect(composition.getText()).toBe(TEXT);
- FallbackCompositionState.release(composition);
+ FallbackCompositionState.initialize(getInput());
+ expect(FallbackCompositionState.getText()).toBe(TEXT);
+ FallbackCompositionState.reset();
- composition = FallbackCompositionState.getPooled(getTextarea());
- expect(composition.getText()).toBe(TEXT);
- FallbackCompositionState.release(composition);
+ FallbackCompositionState.initialize(getTextarea());
+ expect(FallbackCompositionState.getText()).toBe(TEXT);
+ FallbackCompositionState.reset();
- composition = FallbackCompositionState.getPooled(getContentEditable());
- expect(composition.getText()).toBe(TEXT);
- FallbackCompositionState.release(composition);
+ FallbackCompositionState.initialize(getContentEditable());
+ expect(FallbackCompositionState.getText()).toBe(TEXT);
+ FallbackCompositionState.reset();
});
describe('Extract fallback data inserted at collapsed cursor', () => {
diff --git a/src/renderers/shared/shared/event/SyntheticEvent.js b/src/renderers/shared/shared/event/SyntheticEvent.js
index 9dbb2dd0de2..d9788898873 100644
--- a/src/renderers/shared/shared/event/SyntheticEvent.js
+++ b/src/renderers/shared/shared/event/SyntheticEvent.js
@@ -13,12 +13,12 @@
'use strict';
-var PooledClass = require('PooledClass');
-
var emptyFunction = require('fbjs/lib/emptyFunction');
+var invariant = require('fbjs/lib/invariant');
var didWarnForAddedNewProperty = false;
var isProxySupported = typeof Proxy === 'function';
+var EVENT_POOL_SIZE = 10;
if (__DEV__) {
var warning = require('fbjs/lib/warning');
@@ -232,8 +232,7 @@ SyntheticEvent.augmentClass = function(Class, Interface) {
Class.Interface = Object.assign({}, Super.Interface, Interface);
Class.augmentClass = Super.augmentClass;
-
- PooledClass.addPoolingTo(Class, PooledClass.fourArgumentPooler);
+ addEventPoolingTo(Class);
};
/** Proxying after everything set on SyntheticEvent
@@ -274,7 +273,7 @@ if (__DEV__) {
}
}
-PooledClass.addPoolingTo(SyntheticEvent, PooledClass.fourArgumentPooler);
+addEventPoolingTo(SyntheticEvent);
module.exports = SyntheticEvent;
@@ -322,3 +321,42 @@ function getPooledWarningPropertyDefinition(propName, getVal) {
);
}
}
+
+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) {
+ var 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;
+}