UNPKG

6.07 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var PooledClass = require('./PooledClass');
12var ReactElement = require('./ReactElement');
13
14var emptyFunction = require('fbjs/lib/emptyFunction');
15var traverseAllChildren = require('./traverseAllChildren');
16
17var twoArgumentPooler = PooledClass.twoArgumentPooler;
18var fourArgumentPooler = PooledClass.fourArgumentPooler;
19
20var userProvidedKeyEscapeRegex = /\/+/g;
21function escapeUserProvidedKey(text) {
22 return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
23}
24
25/**
26 * PooledClass representing the bookkeeping associated with performing a child
27 * traversal. Allows avoiding binding callbacks.
28 *
29 * @constructor ForEachBookKeeping
30 * @param {!function} forEachFunction Function to perform traversal with.
31 * @param {?*} forEachContext Context to perform context with.
32 */
33function ForEachBookKeeping(forEachFunction, forEachContext) {
34 this.func = forEachFunction;
35 this.context = forEachContext;
36 this.count = 0;
37}
38ForEachBookKeeping.prototype.destructor = function () {
39 this.func = null;
40 this.context = null;
41 this.count = 0;
42};
43PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
44
45function forEachSingleChild(bookKeeping, child, name) {
46 var func = bookKeeping.func,
47 context = bookKeeping.context;
48
49 func.call(context, child, bookKeeping.count++);
50}
51
52/**
53 * Iterates through children that are typically specified as `props.children`.
54 *
55 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.foreach
56 *
57 * The provided forEachFunc(child, index) will be called for each
58 * leaf child.
59 *
60 * @param {?*} children Children tree container.
61 * @param {function(*, int)} forEachFunc
62 * @param {*} forEachContext Context for forEachContext.
63 */
64function forEachChildren(children, forEachFunc, forEachContext) {
65 if (children == null) {
66 return children;
67 }
68 var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
69 traverseAllChildren(children, forEachSingleChild, traverseContext);
70 ForEachBookKeeping.release(traverseContext);
71}
72
73/**
74 * PooledClass representing the bookkeeping associated with performing a child
75 * mapping. Allows avoiding binding callbacks.
76 *
77 * @constructor MapBookKeeping
78 * @param {!*} mapResult Object containing the ordered map of results.
79 * @param {!function} mapFunction Function to perform mapping with.
80 * @param {?*} mapContext Context to perform mapping with.
81 */
82function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
83 this.result = mapResult;
84 this.keyPrefix = keyPrefix;
85 this.func = mapFunction;
86 this.context = mapContext;
87 this.count = 0;
88}
89MapBookKeeping.prototype.destructor = function () {
90 this.result = null;
91 this.keyPrefix = null;
92 this.func = null;
93 this.context = null;
94 this.count = 0;
95};
96PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);
97
98function mapSingleChildIntoContext(bookKeeping, child, childKey) {
99 var result = bookKeeping.result,
100 keyPrefix = bookKeeping.keyPrefix,
101 func = bookKeeping.func,
102 context = bookKeeping.context;
103
104
105 var mappedChild = func.call(context, child, bookKeeping.count++);
106 if (Array.isArray(mappedChild)) {
107 mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument);
108 } else if (mappedChild != null) {
109 if (ReactElement.isValidElement(mappedChild)) {
110 mappedChild = ReactElement.cloneAndReplaceKey(mappedChild,
111 // Keep both the (mapped) and old keys if they differ, just as
112 // traverseAllChildren used to do for objects as children
113 keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey);
114 }
115 result.push(mappedChild);
116 }
117}
118
119function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
120 var escapedPrefix = '';
121 if (prefix != null) {
122 escapedPrefix = escapeUserProvidedKey(prefix) + '/';
123 }
124 var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context);
125 traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
126 MapBookKeeping.release(traverseContext);
127}
128
129/**
130 * Maps children that are typically specified as `props.children`.
131 *
132 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.map
133 *
134 * The provided mapFunction(child, key, index) will be called for each
135 * leaf child.
136 *
137 * @param {?*} children Children tree container.
138 * @param {function(*, int)} func The map function.
139 * @param {*} context Context for mapFunction.
140 * @return {object} Object containing the ordered map of results.
141 */
142function mapChildren(children, func, context) {
143 if (children == null) {
144 return children;
145 }
146 var result = [];
147 mapIntoWithKeyPrefixInternal(children, result, null, func, context);
148 return result;
149}
150
151function forEachSingleChildDummy(traverseContext, child, name) {
152 return null;
153}
154
155/**
156 * Count the number of children that are typically specified as
157 * `props.children`.
158 *
159 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.count
160 *
161 * @param {?*} children Children tree container.
162 * @return {number} The number of children.
163 */
164function countChildren(children, context) {
165 return traverseAllChildren(children, forEachSingleChildDummy, null);
166}
167
168/**
169 * Flatten a children object (typically specified as `props.children`) and
170 * return an array with appropriately re-keyed children.
171 *
172 * See https://facebook.github.io/react/docs/top-level-api.html#react.children.toarray
173 */
174function toArray(children) {
175 var result = [];
176 mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument);
177 return result;
178}
179
180var ReactChildren = {
181 forEach: forEachChildren,
182 map: mapChildren,
183 mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,
184 count: countChildren,
185 toArray: toArray
186};
187
188module.exports = ReactChildren;
\No newline at end of file