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;