@samitouri / QOS-React / commits / 240b84ed8e

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

Dan Abramov committed Jul 20, 2017 at 15:56 UTC 240b84ed8e1db715d759afaae85033718a0b24e1
5 files changed +251 -280
scripts/rollup/packaging.js
+1 -1
@@ -24,7 +24,7 @@ const facebookWWWSrcDependencies = [
24
25 // these files need to be copied to the react-native build
26 const reactNativeSrcDependencies = [
27 - 'src/shared/utils/PooledClass.js',
27 + 'src/renderers/shared/utils/PooledClass.js',
28 'src/renderers/shared/fiber/isomorphic/ReactTypes.js',
29 'src/renderers/native/ReactNativeTypes.js',
30 ];
scripts/rollup/results.json
+12 -12
@@ -1,28 +1,28 @@
1 {
2 "bundleSizes": {
3 "react.development.js (UMD_DEV)": {
4 - "size": 69376,
5 - "gzip": 17780
4 + "size": 65104,
5 + "gzip": 16679
6 },
7 "react.production.min.js (UMD_PROD)": {
8 - "size": 7585,
9 - "gzip": 3002
8 + "size": 6478,
9 + "gzip": 2700
10 },
11 "react.development.js (NODE_DEV)": {
12 - "size": 59667,
13 - "gzip": 15411
12 + "size": 55397,
13 + "gzip": 14325
14 },
15 "react.production.min.js (NODE_PROD)": {
16 - "size": 6451,
17 - "gzip": 2576
16 + "size": 5343,
17 + "gzip": 2268
18 },
19 "React-dev.js (FB_DEV)": {
20 - "size": 56905,
21 - "gzip": 14622
20 + "size": 52655,
21 + "gzip": 13543
22 },
23 "React-prod.js (FB_PROD)": {
24 - "size": 27099,
25 - "gzip": 7215
24 + "size": 24178,
25 + "gzip": 6601
26 },
27 "react-dom.development.js (UMD_DEV)": {
28 "size": 624799,
src/isomorphic/children/ReactChildren.js
+238 -53
@@ -11,39 +11,251 @@
11
12 'use strict';
13
14 -var PooledClass = require('PooledClass');
14 var ReactElement = require('ReactElement');
15
16 var emptyFunction = require('fbjs/lib/emptyFunction');
18 -var traverseAllChildren = require('traverseAllChildren');
17 +var invariant = require('fbjs/lib/invariant');
18
20 -var twoArgumentPooler = PooledClass.twoArgumentPooler;
21 -var fourArgumentPooler = PooledClass.fourArgumentPooler;
19 +if (__DEV__) {
20 + var warning = require('fbjs/lib/warning');
21 + var {getStackAddendum} = require('ReactDebugCurrentFrame');
22 +}
23 +
24 +var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
25 +var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
26 +// The Symbol used to tag the ReactElement type. If there is no native Symbol
27 +// nor polyfill, then a plain number is used for performance.
28 +var REACT_ELEMENT_TYPE =
29 + (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
30 + 0xeac7;
31 +
32 +var SEPARATOR = '.';
33 +var SUBSEPARATOR = ':';
34 +
35 +/**
36 + * Escape and wrap key so it is safe to use as a reactid
37 + *
38 + * @param {string} key to be escaped.
39 + * @return {string} the escaped key.
40 + */
41 +function escape(key: string): string {
42 + var escapeRegex = /[=:]/g;
43 + var escaperLookup = {
44 + '=': '=0',
45 + ':': '=2',
46 + };
47 + var escapedString = ('' + key).replace(escapeRegex, function(match) {
48 + return escaperLookup[match];
49 + });
50 +
51 + return '$' + escapedString;
52 +}
53 +
54 +/**
55 + * TODO: Test that a single child and an array with one item have the same key
56 + * pattern.
57 + */
58 +
59 +var didWarnAboutMaps = false;
60
61 var userProvidedKeyEscapeRegex = /\/+/g;
62 function escapeUserProvidedKey(text) {
63 return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
64 }
65
66 +var POOL_SIZE = 10;
67 +var traverseContextPool = [];
68 +function getPooledTraverseContext(
69 + mapResult,
70 + keyPrefix,
71 + mapFunction,
72 + mapContext,
73 +) {
74 + if (traverseContextPool.length) {
75 + var traverseContext = traverseContextPool.pop();
76 + traverseContext.result = mapResult;
77 + traverseContext.keyPrefix = keyPrefix;
78 + traverseContext.func = mapFunction;
79 + traverseContext.context = mapContext;
80 + traverseContext.count = 0;
81 + return traverseContext;
82 + } else {
83 + return {
84 + result: mapResult,
85 + keyPrefix: keyPrefix,
86 + func: mapFunction,
87 + context: mapContext,
88 + count: 0,
89 + };
90 + }
91 +}
92 +
93 +function releaseTraverseContext(traverseContext) {
94 + traverseContext.result = null;
95 + traverseContext.keyPrefix = null;
96 + traverseContext.func = null;
97 + traverseContext.context = null;
98 + traverseContext.count = 0;
99 + if (traverseContextPool.length < POOL_SIZE) {
100 + traverseContextPool.push(traverseContext);
101 + }
102 +}
103 +
104 +/**
105 + * @param {?*} children Children tree container.
106 + * @param {!string} nameSoFar Name of the key path so far.
107 + * @param {!function} callback Callback to invoke with each child found.
108 + * @param {?*} traverseContext Used to pass information throughout the traversal
109 + * process.
110 + * @return {!number} The number of children in this subtree.
111 + */
112 +function traverseAllChildrenImpl(
113 + children,
114 + nameSoFar,
115 + callback,
116 + traverseContext,
117 +) {
118 + var type = typeof children;
119 +
120 + if (type === 'undefined' || type === 'boolean') {
121 + // All of the above are perceived as null.
122 + children = null;
123 + }
124 +
125 + if (
126 + children === null ||
127 + type === 'string' ||
128 + type === 'number' ||
129 + // The following is inlined from ReactElement. This means we can optimize
130 + // some checks. React Fiber also inlines this logic for similar purposes.
131 + (type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
132 + ) {
133 + callback(
134 + traverseContext,
135 + children,
136 + // If it's the only child, treat the name as if it was wrapped in an array
137 + // so that it's consistent if the number of children grows.
138 + nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
139 + );
140 + return 1;
141 + }
142 +
143 + var child;
144 + var nextName;
145 + var subtreeCount = 0; // Count of children found in the current subtree.
146 + var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
147 +
148 + if (Array.isArray(children)) {
149 + for (var i = 0; i < children.length; i++) {
150 + child = children[i];
151 + nextName = nextNamePrefix + getComponentKey(child, i);
152 + subtreeCount += traverseAllChildrenImpl(
153 + child,
154 + nextName,
155 + callback,
156 + traverseContext,
157 + );
158 + }
159 + } else {
160 + var iteratorFn =
161 + (ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
162 + children[FAUX_ITERATOR_SYMBOL];
163 + if (typeof iteratorFn === 'function') {
164 + if (__DEV__) {
165 + // Warn about using Maps as children
166 + if (iteratorFn === children.entries) {
167 + warning(
168 + didWarnAboutMaps,
169 + 'Using Maps as children is unsupported and will likely yield ' +
170 + 'unexpected results. Convert it to a sequence/iterable of keyed ' +
171 + 'ReactElements instead.%s',
172 + getStackAddendum(),
173 + );
174 + didWarnAboutMaps = true;
175 + }
176 + }
177 +
178 + var iterator = iteratorFn.call(children);
179 + var step;
180 + var ii = 0;
181 + while (!(step = iterator.next()).done) {
182 + child = step.value;
183 + nextName = nextNamePrefix + getComponentKey(child, ii++);
184 + subtreeCount += traverseAllChildrenImpl(
185 + child,
186 + nextName,
187 + callback,
188 + traverseContext,
189 + );
190 + }
191 + } else if (type === 'object') {
192 + var addendum = '';
193 + if (__DEV__) {
194 + addendum =
195 + ' If you meant to render a collection of children, use an array ' +
196 + 'instead.' +
197 + getStackAddendum();
198 + }
199 + var childrenString = '' + children;
200 + invariant(
201 + false,
202 + 'Objects are not valid as a React child (found: %s).%s',
203 + childrenString === '[object Object]'
204 + ? 'object with keys {' + Object.keys(children).join(', ') + '}'
205 + : childrenString,
206 + addendum,
207 + );
208 + }
209 + }
210 +
211 + return subtreeCount;
212 +}
213 +
214 /**
29 - * PooledClass representing the bookkeeping associated with performing a child
30 - * traversal. Allows avoiding binding callbacks.
215 + * Traverses children that are typically specified as `props.children`, but
216 + * might also be specified through attributes:
217 *
32 - * @constructor ForEachBookKeeping
33 - * @param {!function} forEachFunction Function to perform traversal with.
34 - * @param {?*} forEachContext Context to perform context with.
218 + * - `traverseAllChildren(this.props.children, ...)`
219 + * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
220 + *
221 + * The `traverseContext` is an optional argument that is passed through the
222 + * entire traversal. It can be used to store accumulations or anything else that
223 + * the callback might find relevant.
224 + *
225 + * @param {?*} children Children tree object.
226 + * @param {!function} callback To invoke upon traversing each child.
227 + * @param {?*} traverseContext Context for traversal.
228 + * @return {!number} The number of children in this subtree.
229 */
36 -function ForEachBookKeeping(forEachFunction, forEachContext) {
37 - this.func = forEachFunction;
38 - this.context = forEachContext;
39 - this.count = 0;
230 +function traverseAllChildren(children, callback, traverseContext) {
231 + if (children == null) {
232 + return 0;
233 + }
234 +
235 + return traverseAllChildrenImpl(children, '', callback, traverseContext);
236 +}
237 +
238 +/**
239 + * Generate a key string that identifies a component within a set.
240 + *
241 + * @param {*} component A component that could contain a manual key.
242 + * @param {number} index Index that is used if a manual key is not provided.
243 + * @return {string}
244 + */
245 +function getComponentKey(component, index) {
246 + // Do some typechecking here since we call this blindly. We want to ensure
247 + // that we don't block potential future ES APIs.
248 + if (
249 + typeof component === 'object' &&
250 + component !== null &&
251 + component.key != null
252 + ) {
253 + // Explicit key
254 + return escape(component.key);
255 + }
256 + // Implicit key determined by the index in the set
257 + return index.toString(36);
258 }
41 -ForEachBookKeeping.prototype.destructor = function() {
42 - this.func = null;
43 - this.context = null;
44 - this.count = 0;
45 -};
46 -PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
259
260 function forEachSingleChild(bookKeeping, child, name) {
261 var {func, context} = bookKeeping;
@@ -66,39 +278,16 @@ function forEachChildren(children, forEachFunc, forEachContext) {
278 if (children == null) {
279 return children;
280 }
69 - var traverseContext = ForEachBookKeeping.getPooled(
281 + var traverseContext = getPooledTraverseContext(
282 + null,
283 + null,
284 forEachFunc,
285 forEachContext,
286 );
287 traverseAllChildren(children, forEachSingleChild, traverseContext);
74 - ForEachBookKeeping.release(traverseContext);
288 + releaseTraverseContext(traverseContext);
289 }
290
77 -/**
78 - * PooledClass representing the bookkeeping associated with performing a child
79 - * mapping. Allows avoiding binding callbacks.
80 - *
81 - * @constructor MapBookKeeping
82 - * @param {!*} mapResult Object containing the ordered map of results.
83 - * @param {!function} mapFunction Function to perform mapping with.
84 - * @param {?*} mapContext Context to perform mapping with.
85 - */
86 -function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
87 - this.result = mapResult;
88 - this.keyPrefix = keyPrefix;
89 - this.func = mapFunction;
90 - this.context = mapContext;
91 - this.count = 0;
92 -}
93 -MapBookKeeping.prototype.destructor = function() {
94 - this.result = null;
95 - this.keyPrefix = null;
96 - this.func = null;
97 - this.context = null;
98 - this.count = 0;
99 -};
100 -PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);
101 -
291 function mapSingleChildIntoContext(bookKeeping, child, childKey) {
292 var {result, keyPrefix, func, context} = bookKeeping;
293
@@ -132,14 +321,14 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
321 if (prefix != null) {
322 escapedPrefix = escapeUserProvidedKey(prefix) + '/';
323 }
135 - var traverseContext = MapBookKeeping.getPooled(
324 + var traverseContext = getPooledTraverseContext(
325 array,
326 escapedPrefix,
327 func,
328 context,
329 );
330 traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
142 - MapBookKeeping.release(traverseContext);
331 + releaseTraverseContext(traverseContext);
332 }
333
334 /**
@@ -164,10 +353,6 @@ function mapChildren(children, func, context) {
353 return result;
354 }
355
167 -function forEachSingleChildDummy(traverseContext, child, name) {
168 - return null;
169 -}
170 -
356 /**
357 * Count the number of children that are typically specified as
358 * `props.children`.
@@ -178,7 +363,7 @@ function forEachSingleChildDummy(traverseContext, child, name) {
363 * @return {number} The number of children.
364 */
365 function countChildren(children, context) {
181 - return traverseAllChildren(children, forEachSingleChildDummy, null);
366 + return traverseAllChildren(children, emptyFunction.thatReturnsNull, null);
367 }
368
369 /**
src/isomorphic/children/traverseAllChildren.js deleted
-214
@@ -1,214 +0,0 @@
1 -/**
2 - * Copyright 2013-present, Facebook, Inc.
3 - * All rights reserved.
4 - *
5 - * This source code is licensed under the BSD-style license found in the
6 - * LICENSE file in the root directory of this source tree. An additional grant
7 - * of patent rights can be found in the PATENTS file in the same directory.
8 - *
9 - * @providesModule traverseAllChildren
10 - */
11 -
12 -'use strict';
13 -
14 -var invariant = require('fbjs/lib/invariant');
15 -
16 -var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
17 -var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
18 -// The Symbol used to tag the ReactElement type. If there is no native Symbol
19 -// nor polyfill, then a plain number is used for performance.
20 -var REACT_ELEMENT_TYPE =
21 - (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
22 - 0xeac7;
23 -
24 -if (__DEV__) {
25 - var warning = require('fbjs/lib/warning');
26 - var {getStackAddendum} = require('ReactDebugCurrentFrame');
27 -}
28 -
29 -var SEPARATOR = '.';
30 -var SUBSEPARATOR = ':';
31 -
32 -/**
33 - * Escape and wrap key so it is safe to use as a reactid
34 - *
35 - * @param {string} key to be escaped.
36 - * @return {string} the escaped key.
37 - */
38 -function escape(key: string): string {
39 - var escapeRegex = /[=:]/g;
40 - var escaperLookup = {
41 - '=': '=0',
42 - ':': '=2',
43 - };
44 - var escapedString = ('' + key).replace(escapeRegex, function(match) {
45 - return escaperLookup[match];
46 - });
47 -
48 - return '$' + escapedString;
49 -}
50 -
51 -/**
52 - * TODO: Test that a single child and an array with one item have the same key
53 - * pattern.
54 - */
55 -
56 -var didWarnAboutMaps = false;
57 -
58 -/**
59 - * Generate a key string that identifies a component within a set.
60 - *
61 - * @param {*} component A component that could contain a manual key.
62 - * @param {number} index Index that is used if a manual key is not provided.
63 - * @return {string}
64 - */
65 -function getComponentKey(component, index) {
66 - // Do some typechecking here since we call this blindly. We want to ensure
67 - // that we don't block potential future ES APIs.
68 - if (
69 - typeof component === 'object' &&
70 - component !== null &&
71 - component.key != null
72 - ) {
73 - // Explicit key
74 - return escape(component.key);
75 - }
76 - // Implicit key determined by the index in the set
77 - return index.toString(36);
78 -}
79 -
80 -/**
81 - * @param {?*} children Children tree container.
82 - * @param {!string} nameSoFar Name of the key path so far.
83 - * @param {!function} callback Callback to invoke with each child found.
84 - * @param {?*} traverseContext Used to pass information throughout the traversal
85 - * process.
86 - * @return {!number} The number of children in this subtree.
87 - */
88 -function traverseAllChildrenImpl(
89 - children,
90 - nameSoFar,
91 - callback,
92 - traverseContext,
93 -) {
94 - var type = typeof children;
95 -
96 - if (type === 'undefined' || type === 'boolean') {
97 - // All of the above are perceived as null.
98 - children = null;
99 - }
100 -
101 - if (
102 - children === null ||
103 - type === 'string' ||
104 - type === 'number' ||
105 - // The following is inlined from ReactElement. This means we can optimize
106 - // some checks. React Fiber also inlines this logic for similar purposes.
107 - (type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
108 - ) {
109 - callback(
110 - traverseContext,
111 - children,
112 - // If it's the only child, treat the name as if it was wrapped in an array
113 - // so that it's consistent if the number of children grows.
114 - nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
115 - );
116 - return 1;
117 - }
118 -
119 - var child;
120 - var nextName;
121 - var subtreeCount = 0; // Count of children found in the current subtree.
122 - var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
123 -
124 - if (Array.isArray(children)) {
125 - for (var i = 0; i < children.length; i++) {
126 - child = children[i];
127 - nextName = nextNamePrefix + getComponentKey(child, i);
128 - subtreeCount += traverseAllChildrenImpl(
129 - child,
130 - nextName,
131 - callback,
132 - traverseContext,
133 - );
134 - }
135 - } else {
136 - var iteratorFn =
137 - (ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
138 - children[FAUX_ITERATOR_SYMBOL];
139 - if (typeof iteratorFn === 'function') {
140 - if (__DEV__) {
141 - // Warn about using Maps as children
142 - if (iteratorFn === children.entries) {
143 - warning(
144 - didWarnAboutMaps,
145 - 'Using Maps as children is unsupported and will likely yield ' +
146 - 'unexpected results. Convert it to a sequence/iterable of keyed ' +
147 - 'ReactElements instead.%s',
148 - getStackAddendum(),
149 - );
150 - didWarnAboutMaps = true;
151 - }
152 - }
153 -
154 - var iterator = iteratorFn.call(children);
155 - var step;
156 - var ii = 0;
157 - while (!(step = iterator.next()).done) {
158 - child = step.value;
159 - nextName = nextNamePrefix + getComponentKey(child, ii++);
160 - subtreeCount += traverseAllChildrenImpl(
161 - child,
162 - nextName,
163 - callback,
164 - traverseContext,
165 - );
166 - }
167 - } else if (type === 'object') {
168 - var addendum = '';
169 - if (__DEV__) {
170 - addendum =
171 - ' If you meant to render a collection of children, use an array ' +
172 - 'instead.' +
173 - getStackAddendum();
174 - }
175 - var childrenString = '' + children;
176 - invariant(
177 - false,
178 - 'Objects are not valid as a React child (found: %s).%s',
179 - childrenString === '[object Object]'
180 - ? 'object with keys {' + Object.keys(children).join(', ') + '}'
181 - : childrenString,
182 - addendum,
183 - );
184 - }
185 - }
186 -
187 - return subtreeCount;
188 -}
189 -
190 -/**
191 - * Traverses children that are typically specified as `props.children`, but
192 - * might also be specified through attributes:
193 - *
194 - * - `traverseAllChildren(this.props.children, ...)`
195 - * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
196 - *
197 - * The `traverseContext` is an optional argument that is passed through the
198 - * entire traversal. It can be used to store accumulations or anything else that
199 - * the callback might find relevant.
200 - *
201 - * @param {?*} children Children tree object.
202 - * @param {!function} callback To invoke upon traversing each child.
203 - * @param {?*} traverseContext Context for traversal.
204 - * @return {!number} The number of children in this subtree.
205 - */
206 -function traverseAllChildren(children, callback, traverseContext) {
207 - if (children == null) {
208 - return 0;
209 - }
210 -
211 - return traverseAllChildrenImpl(children, '', callback, traverseContext);
212 -}
213 -
214 -module.exports = traverseAllChildren;
src/renderers/shared/utils/PooledClass.js renamed