-
Notifications
You must be signed in to change notification settings - Fork 50.2k
Remove PooledClass from isomorphic build #10227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
dbfcea3
8005895
cd36b0a
c88787d
ab9a573
5d8cd2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,39 +11,213 @@ | |
|
|
||
| '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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey,
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably accidentally or by copy paste. Want to send a PR to Flow-ify it?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep! I can delete useless flow typechecking or add comment flow. If I add flow comment maybe I can try to update types for all file? If you want this.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
| 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, '$&/'); | ||
| } | ||
|
|
||
| /** | ||
| * 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: | ||
| * | ||
| * @constructor ForEachBookKeeping | ||
| * @param {!function} forEachFunction Function to perform traversal with. | ||
| * @param {?*} forEachContext Context to perform context with. | ||
| * - `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 ForEachBookKeeping(forEachFunction, forEachContext) { | ||
| this.func = forEachFunction; | ||
| this.context = forEachContext; | ||
| this.count = 0; | ||
| } | ||
| ForEachBookKeeping.prototype.destructor = function() { | ||
| this.func = null; | ||
| this.context = null; | ||
| this.count = 0; | ||
| }; | ||
| PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler); | ||
| 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. | ||
| * | ||
| * @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); | ||
| } | ||
|
|
||
| function forEachSingleChild(bookKeeping, child, name) { | ||
| var {func, context} = bookKeeping; | ||
|
|
@@ -66,38 +240,53 @@ 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); | ||
| 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); | ||
| } | ||
| } | ||
|
|
||
| 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,7 +353,7 @@ function mapChildren(children, func, context) { | |
| return result; | ||
| } | ||
|
|
||
| function forEachSingleChildDummy(traverseContext, child, name) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're already importing the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed. |
||
| function forEachSingleChildDummy() { | ||
| return null; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoa. why is FB_PROD so much larger than NODE_PROD/UMD_PROD? Legacy files that are being consumed internally yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's just not minified. We might minify it eventually but for now it doesn't seem necessary (it goes through our pipeline anyway), and it's easier to see what happens during the sync.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Great point. 👍