mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Remove PooledClass from isomorphic build (#10227)
* Inline traverseAllChildren into ReactChildren * Remove ForEachBookKeeping * Inline traversal pooling logic into ReactChildren * Reuse emptyFunction for dummy callback * Move code around * Record sizes
This commit is contained in:
@@ -24,7 +24,7 @@ const facebookWWWSrcDependencies = [
|
||||
|
||||
// these files need to be copied to the react-native build
|
||||
const reactNativeSrcDependencies = [
|
||||
'src/shared/utils/PooledClass.js',
|
||||
'src/renderers/shared/utils/PooledClass.js',
|
||||
'src/renderers/shared/fiber/isomorphic/ReactTypes.js',
|
||||
'src/renderers/native/ReactNativeTypes.js',
|
||||
];
|
||||
|
||||
+12
-12
@@ -1,28 +1,28 @@
|
||||
{
|
||||
"bundleSizes": {
|
||||
"react.development.js (UMD_DEV)": {
|
||||
"size": 69376,
|
||||
"gzip": 17780
|
||||
"size": 65104,
|
||||
"gzip": 16679
|
||||
},
|
||||
"react.production.min.js (UMD_PROD)": {
|
||||
"size": 7585,
|
||||
"gzip": 3002
|
||||
"size": 6478,
|
||||
"gzip": 2700
|
||||
},
|
||||
"react.development.js (NODE_DEV)": {
|
||||
"size": 59667,
|
||||
"gzip": 15411
|
||||
"size": 55397,
|
||||
"gzip": 14325
|
||||
},
|
||||
"react.production.min.js (NODE_PROD)": {
|
||||
"size": 6451,
|
||||
"gzip": 2576
|
||||
"size": 5343,
|
||||
"gzip": 2268
|
||||
},
|
||||
"React-dev.js (FB_DEV)": {
|
||||
"size": 56905,
|
||||
"gzip": 14622
|
||||
"size": 52655,
|
||||
"gzip": 13543
|
||||
},
|
||||
"React-prod.js (FB_PROD)": {
|
||||
"size": 27099,
|
||||
"gzip": 7215
|
||||
"size": 24178,
|
||||
"gzip": 6601
|
||||
},
|
||||
"react-dom.development.js (UMD_DEV)": {
|
||||
"size": 624799,
|
||||
|
||||
@@ -11,39 +11,251 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var PooledClass = require('PooledClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
|
||||
var emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
var traverseAllChildren = require('traverseAllChildren');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
|
||||
var twoArgumentPooler = PooledClass.twoArgumentPooler;
|
||||
var fourArgumentPooler = PooledClass.fourArgumentPooler;
|
||||
if (__DEV__) {
|
||||
var warning = require('fbjs/lib/warning');
|
||||
var {getStackAddendum} = require('ReactDebugCurrentFrame');
|
||||
}
|
||||
|
||||
var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
|
||||
var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
|
||||
// The Symbol used to tag the ReactElement type. If there is no native Symbol
|
||||
// nor polyfill, then a plain number is used for performance.
|
||||
var REACT_ELEMENT_TYPE =
|
||||
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
|
||||
0xeac7;
|
||||
|
||||
var SEPARATOR = '.';
|
||||
var SUBSEPARATOR = ':';
|
||||
|
||||
/**
|
||||
* Escape and wrap key so it is safe to use as a reactid
|
||||
*
|
||||
* @param {string} key to be escaped.
|
||||
* @return {string} the escaped key.
|
||||
*/
|
||||
function escape(key: string): string {
|
||||
var escapeRegex = /[=:]/g;
|
||||
var escaperLookup = {
|
||||
'=': '=0',
|
||||
':': '=2',
|
||||
};
|
||||
var escapedString = ('' + key).replace(escapeRegex, function(match) {
|
||||
return escaperLookup[match];
|
||||
});
|
||||
|
||||
return '$' + escapedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Test that a single child and an array with one item have the same key
|
||||
* pattern.
|
||||
*/
|
||||
|
||||
var didWarnAboutMaps = false;
|
||||
|
||||
var userProvidedKeyEscapeRegex = /\/+/g;
|
||||
function escapeUserProvidedKey(text) {
|
||||
return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
|
||||
}
|
||||
|
||||
var POOL_SIZE = 10;
|
||||
var traverseContextPool = [];
|
||||
function getPooledTraverseContext(
|
||||
mapResult,
|
||||
keyPrefix,
|
||||
mapFunction,
|
||||
mapContext,
|
||||
) {
|
||||
if (traverseContextPool.length) {
|
||||
var traverseContext = traverseContextPool.pop();
|
||||
traverseContext.result = mapResult;
|
||||
traverseContext.keyPrefix = keyPrefix;
|
||||
traverseContext.func = mapFunction;
|
||||
traverseContext.context = mapContext;
|
||||
traverseContext.count = 0;
|
||||
return traverseContext;
|
||||
} else {
|
||||
return {
|
||||
result: mapResult,
|
||||
keyPrefix: keyPrefix,
|
||||
func: mapFunction,
|
||||
context: mapContext,
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function releaseTraverseContext(traverseContext) {
|
||||
traverseContext.result = null;
|
||||
traverseContext.keyPrefix = null;
|
||||
traverseContext.func = null;
|
||||
traverseContext.context = null;
|
||||
traverseContext.count = 0;
|
||||
if (traverseContextPool.length < POOL_SIZE) {
|
||||
traverseContextPool.push(traverseContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PooledClass representing the bookkeeping associated with performing a child
|
||||
* traversal. Allows avoiding binding callbacks.
|
||||
* @param {?*} children Children tree container.
|
||||
* @param {!string} nameSoFar Name of the key path so far.
|
||||
* @param {!function} callback Callback to invoke with each child found.
|
||||
* @param {?*} traverseContext Used to pass information throughout the traversal
|
||||
* process.
|
||||
* @return {!number} The number of children in this subtree.
|
||||
*/
|
||||
function traverseAllChildrenImpl(
|
||||
children,
|
||||
nameSoFar,
|
||||
callback,
|
||||
traverseContext,
|
||||
) {
|
||||
var type = typeof children;
|
||||
|
||||
if (type === 'undefined' || type === 'boolean') {
|
||||
// All of the above are perceived as null.
|
||||
children = null;
|
||||
}
|
||||
|
||||
if (
|
||||
children === null ||
|
||||
type === 'string' ||
|
||||
type === 'number' ||
|
||||
// The following is inlined from ReactElement. This means we can optimize
|
||||
// some checks. React Fiber also inlines this logic for similar purposes.
|
||||
(type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
|
||||
) {
|
||||
callback(
|
||||
traverseContext,
|
||||
children,
|
||||
// If it's the only child, treat the name as if it was wrapped in an array
|
||||
// so that it's consistent if the number of children grows.
|
||||
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var child;
|
||||
var nextName;
|
||||
var subtreeCount = 0; // Count of children found in the current subtree.
|
||||
var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
|
||||
|
||||
if (Array.isArray(children)) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
child = children[i];
|
||||
nextName = nextNamePrefix + getComponentKey(child, i);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
var iteratorFn =
|
||||
(ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
|
||||
children[FAUX_ITERATOR_SYMBOL];
|
||||
if (typeof iteratorFn === 'function') {
|
||||
if (__DEV__) {
|
||||
// Warn about using Maps as children
|
||||
if (iteratorFn === children.entries) {
|
||||
warning(
|
||||
didWarnAboutMaps,
|
||||
'Using Maps as children is unsupported and will likely yield ' +
|
||||
'unexpected results. Convert it to a sequence/iterable of keyed ' +
|
||||
'ReactElements instead.%s',
|
||||
getStackAddendum(),
|
||||
);
|
||||
didWarnAboutMaps = true;
|
||||
}
|
||||
}
|
||||
|
||||
var iterator = iteratorFn.call(children);
|
||||
var step;
|
||||
var ii = 0;
|
||||
while (!(step = iterator.next()).done) {
|
||||
child = step.value;
|
||||
nextName = nextNamePrefix + getComponentKey(child, ii++);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext,
|
||||
);
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
var addendum = '';
|
||||
if (__DEV__) {
|
||||
addendum =
|
||||
' If you meant to render a collection of children, use an array ' +
|
||||
'instead.' +
|
||||
getStackAddendum();
|
||||
}
|
||||
var childrenString = '' + children;
|
||||
invariant(
|
||||
false,
|
||||
'Objects are not valid as a React child (found: %s).%s',
|
||||
childrenString === '[object Object]'
|
||||
? 'object with keys {' + Object.keys(children).join(', ') + '}'
|
||||
: childrenString,
|
||||
addendum,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return subtreeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses children that are typically specified as `props.children`, but
|
||||
* might also be specified through attributes:
|
||||
*
|
||||
* - `traverseAllChildren(this.props.children, ...)`
|
||||
* - `traverseAllChildren(this.props.leftPanelChildren, ...)`
|
||||
*
|
||||
* The `traverseContext` is an optional argument that is passed through the
|
||||
* entire traversal. It can be used to store accumulations or anything else that
|
||||
* the callback might find relevant.
|
||||
*
|
||||
* @param {?*} children Children tree object.
|
||||
* @param {!function} callback To invoke upon traversing each child.
|
||||
* @param {?*} traverseContext Context for traversal.
|
||||
* @return {!number} The number of children in this subtree.
|
||||
*/
|
||||
function traverseAllChildren(children, callback, traverseContext) {
|
||||
if (children == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return traverseAllChildrenImpl(children, '', callback, traverseContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key string that identifies a component within a set.
|
||||
*
|
||||
* @constructor ForEachBookKeeping
|
||||
* @param {!function} forEachFunction Function to perform traversal with.
|
||||
* @param {?*} forEachContext Context to perform context with.
|
||||
* @param {*} component A component that could contain a manual key.
|
||||
* @param {number} index Index that is used if a manual key is not provided.
|
||||
* @return {string}
|
||||
*/
|
||||
function ForEachBookKeeping(forEachFunction, forEachContext) {
|
||||
this.func = forEachFunction;
|
||||
this.context = forEachContext;
|
||||
this.count = 0;
|
||||
function getComponentKey(component, index) {
|
||||
// Do some typechecking here since we call this blindly. We want to ensure
|
||||
// that we don't block potential future ES APIs.
|
||||
if (
|
||||
typeof component === 'object' &&
|
||||
component !== null &&
|
||||
component.key != null
|
||||
) {
|
||||
// Explicit key
|
||||
return escape(component.key);
|
||||
}
|
||||
// Implicit key determined by the index in the set
|
||||
return index.toString(36);
|
||||
}
|
||||
ForEachBookKeeping.prototype.destructor = function() {
|
||||
this.func = null;
|
||||
this.context = null;
|
||||
this.count = 0;
|
||||
};
|
||||
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
|
||||
|
||||
function forEachSingleChild(bookKeeping, child, name) {
|
||||
var {func, context} = bookKeeping;
|
||||
@@ -66,39 +278,16 @@ function forEachChildren(children, forEachFunc, forEachContext) {
|
||||
if (children == null) {
|
||||
return children;
|
||||
}
|
||||
var traverseContext = ForEachBookKeeping.getPooled(
|
||||
var traverseContext = getPooledTraverseContext(
|
||||
null,
|
||||
null,
|
||||
forEachFunc,
|
||||
forEachContext,
|
||||
);
|
||||
traverseAllChildren(children, forEachSingleChild, traverseContext);
|
||||
ForEachBookKeeping.release(traverseContext);
|
||||
releaseTraverseContext(traverseContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* PooledClass representing the bookkeeping associated with performing a child
|
||||
* mapping. Allows avoiding binding callbacks.
|
||||
*
|
||||
* @constructor MapBookKeeping
|
||||
* @param {!*} mapResult Object containing the ordered map of results.
|
||||
* @param {!function} mapFunction Function to perform mapping with.
|
||||
* @param {?*} mapContext Context to perform mapping with.
|
||||
*/
|
||||
function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
|
||||
this.result = mapResult;
|
||||
this.keyPrefix = keyPrefix;
|
||||
this.func = mapFunction;
|
||||
this.context = mapContext;
|
||||
this.count = 0;
|
||||
}
|
||||
MapBookKeeping.prototype.destructor = function() {
|
||||
this.result = null;
|
||||
this.keyPrefix = null;
|
||||
this.func = null;
|
||||
this.context = null;
|
||||
this.count = 0;
|
||||
};
|
||||
PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);
|
||||
|
||||
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
|
||||
var {result, keyPrefix, func, context} = bookKeeping;
|
||||
|
||||
@@ -132,14 +321,14 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
|
||||
if (prefix != null) {
|
||||
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
|
||||
}
|
||||
var traverseContext = MapBookKeeping.getPooled(
|
||||
var traverseContext = getPooledTraverseContext(
|
||||
array,
|
||||
escapedPrefix,
|
||||
func,
|
||||
context,
|
||||
);
|
||||
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
|
||||
MapBookKeeping.release(traverseContext);
|
||||
releaseTraverseContext(traverseContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,10 +353,6 @@ function mapChildren(children, func, context) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function forEachSingleChildDummy(traverseContext, child, name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of children that are typically specified as
|
||||
* `props.children`.
|
||||
@@ -178,7 +363,7 @@ function forEachSingleChildDummy(traverseContext, child, name) {
|
||||
* @return {number} The number of children.
|
||||
*/
|
||||
function countChildren(children, context) {
|
||||
return traverseAllChildren(children, forEachSingleChildDummy, null);
|
||||
return traverseAllChildren(children, emptyFunction.thatReturnsNull, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule traverseAllChildren
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
|
||||
var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
|
||||
var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
|
||||
// The Symbol used to tag the ReactElement type. If there is no native Symbol
|
||||
// nor polyfill, then a plain number is used for performance.
|
||||
var REACT_ELEMENT_TYPE =
|
||||
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
|
||||
0xeac7;
|
||||
|
||||
if (__DEV__) {
|
||||
var warning = require('fbjs/lib/warning');
|
||||
var {getStackAddendum} = require('ReactDebugCurrentFrame');
|
||||
}
|
||||
|
||||
var SEPARATOR = '.';
|
||||
var SUBSEPARATOR = ':';
|
||||
|
||||
/**
|
||||
* Escape and wrap key so it is safe to use as a reactid
|
||||
*
|
||||
* @param {string} key to be escaped.
|
||||
* @return {string} the escaped key.
|
||||
*/
|
||||
function escape(key: string): string {
|
||||
var escapeRegex = /[=:]/g;
|
||||
var escaperLookup = {
|
||||
'=': '=0',
|
||||
':': '=2',
|
||||
};
|
||||
var escapedString = ('' + key).replace(escapeRegex, function(match) {
|
||||
return escaperLookup[match];
|
||||
});
|
||||
|
||||
return '$' + escapedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Test that a single child and an array with one item have the same key
|
||||
* pattern.
|
||||
*/
|
||||
|
||||
var didWarnAboutMaps = false;
|
||||
|
||||
/**
|
||||
* Generate a key string that identifies a component within a set.
|
||||
*
|
||||
* @param {*} component A component that could contain a manual key.
|
||||
* @param {number} index Index that is used if a manual key is not provided.
|
||||
* @return {string}
|
||||
*/
|
||||
function getComponentKey(component, index) {
|
||||
// Do some typechecking here since we call this blindly. We want to ensure
|
||||
// that we don't block potential future ES APIs.
|
||||
if (
|
||||
typeof component === 'object' &&
|
||||
component !== null &&
|
||||
component.key != null
|
||||
) {
|
||||
// Explicit key
|
||||
return escape(component.key);
|
||||
}
|
||||
// Implicit key determined by the index in the set
|
||||
return index.toString(36);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?*} children Children tree container.
|
||||
* @param {!string} nameSoFar Name of the key path so far.
|
||||
* @param {!function} callback Callback to invoke with each child found.
|
||||
* @param {?*} traverseContext Used to pass information throughout the traversal
|
||||
* process.
|
||||
* @return {!number} The number of children in this subtree.
|
||||
*/
|
||||
function traverseAllChildrenImpl(
|
||||
children,
|
||||
nameSoFar,
|
||||
callback,
|
||||
traverseContext,
|
||||
) {
|
||||
var type = typeof children;
|
||||
|
||||
if (type === 'undefined' || type === 'boolean') {
|
||||
// All of the above are perceived as null.
|
||||
children = null;
|
||||
}
|
||||
|
||||
if (
|
||||
children === null ||
|
||||
type === 'string' ||
|
||||
type === 'number' ||
|
||||
// The following is inlined from ReactElement. This means we can optimize
|
||||
// some checks. React Fiber also inlines this logic for similar purposes.
|
||||
(type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
|
||||
) {
|
||||
callback(
|
||||
traverseContext,
|
||||
children,
|
||||
// If it's the only child, treat the name as if it was wrapped in an array
|
||||
// so that it's consistent if the number of children grows.
|
||||
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var child;
|
||||
var nextName;
|
||||
var subtreeCount = 0; // Count of children found in the current subtree.
|
||||
var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
|
||||
|
||||
if (Array.isArray(children)) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
child = children[i];
|
||||
nextName = nextNamePrefix + getComponentKey(child, i);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
var iteratorFn =
|
||||
(ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
|
||||
children[FAUX_ITERATOR_SYMBOL];
|
||||
if (typeof iteratorFn === 'function') {
|
||||
if (__DEV__) {
|
||||
// Warn about using Maps as children
|
||||
if (iteratorFn === children.entries) {
|
||||
warning(
|
||||
didWarnAboutMaps,
|
||||
'Using Maps as children is unsupported and will likely yield ' +
|
||||
'unexpected results. Convert it to a sequence/iterable of keyed ' +
|
||||
'ReactElements instead.%s',
|
||||
getStackAddendum(),
|
||||
);
|
||||
didWarnAboutMaps = true;
|
||||
}
|
||||
}
|
||||
|
||||
var iterator = iteratorFn.call(children);
|
||||
var step;
|
||||
var ii = 0;
|
||||
while (!(step = iterator.next()).done) {
|
||||
child = step.value;
|
||||
nextName = nextNamePrefix + getComponentKey(child, ii++);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext,
|
||||
);
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
var addendum = '';
|
||||
if (__DEV__) {
|
||||
addendum =
|
||||
' If you meant to render a collection of children, use an array ' +
|
||||
'instead.' +
|
||||
getStackAddendum();
|
||||
}
|
||||
var childrenString = '' + children;
|
||||
invariant(
|
||||
false,
|
||||
'Objects are not valid as a React child (found: %s).%s',
|
||||
childrenString === '[object Object]'
|
||||
? 'object with keys {' + Object.keys(children).join(', ') + '}'
|
||||
: childrenString,
|
||||
addendum,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return subtreeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses children that are typically specified as `props.children`, but
|
||||
* might also be specified through attributes:
|
||||
*
|
||||
* - `traverseAllChildren(this.props.children, ...)`
|
||||
* - `traverseAllChildren(this.props.leftPanelChildren, ...)`
|
||||
*
|
||||
* The `traverseContext` is an optional argument that is passed through the
|
||||
* entire traversal. It can be used to store accumulations or anything else that
|
||||
* the callback might find relevant.
|
||||
*
|
||||
* @param {?*} children Children tree object.
|
||||
* @param {!function} callback To invoke upon traversing each child.
|
||||
* @param {?*} traverseContext Context for traversal.
|
||||
* @return {!number} The number of children in this subtree.
|
||||
*/
|
||||
function traverseAllChildren(children, callback, traverseContext) {
|
||||
if (children == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return traverseAllChildrenImpl(children, '', callback, traverseContext);
|
||||
}
|
||||
|
||||
module.exports = traverseAllChildren;
|
||||
Reference in New Issue
Block a user