Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81152 views
1
/**
2
* Copyright 2013-2014, 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 ReactElement = require('ReactElement');
15
var ReactInstanceHandles = require('ReactInstanceHandles');
16
17
var invariant = require('invariant');
18
19
var SEPARATOR = ReactInstanceHandles.SEPARATOR;
20
var SUBSEPARATOR = ':';
21
22
/**
23
* TODO: Test that:
24
* 1. `mapChildren` transforms strings and numbers into `ReactTextComponent`.
25
* 2. it('should fail when supplied duplicate key', function() {
26
* 3. That a single child and an array with one item have the same key pattern.
27
* });
28
*/
29
30
var userProvidedKeyEscaperLookup = {
31
'=': '=0',
32
'.': '=1',
33
':': '=2'
34
};
35
36
var userProvidedKeyEscapeRegex = /[=.:]/g;
37
38
function userProvidedKeyEscaper(match) {
39
return userProvidedKeyEscaperLookup[match];
40
}
41
42
/**
43
* Generate a key string that identifies a component within a set.
44
*
45
* @param {*} component A component that could contain a manual key.
46
* @param {number} index Index that is used if a manual key is not provided.
47
* @return {string}
48
*/
49
function getComponentKey(component, index) {
50
if (component && component.key != null) {
51
// Explicit key
52
return wrapUserProvidedKey(component.key);
53
}
54
// Implicit key determined by the index in the set
55
return index.toString(36);
56
}
57
58
/**
59
* Escape a component key so that it is safe to use in a reactid.
60
*
61
* @param {*} key Component key to be escaped.
62
* @return {string} An escaped string.
63
*/
64
function escapeUserProvidedKey(text) {
65
return ('' + text).replace(
66
userProvidedKeyEscapeRegex,
67
userProvidedKeyEscaper
68
);
69
}
70
71
/**
72
* Wrap a `key` value explicitly provided by the user to distinguish it from
73
* implicitly-generated keys generated by a component's index in its parent.
74
*
75
* @param {string} key Value of a user-provided `key` attribute
76
* @return {string}
77
*/
78
function wrapUserProvidedKey(key) {
79
return '$' + escapeUserProvidedKey(key);
80
}
81
82
/**
83
* @param {?*} children Children tree container.
84
* @param {!string} nameSoFar Name of the key path so far.
85
* @param {!number} indexSoFar Number of children encountered until this point.
86
* @param {!function} callback Callback to invoke with each child found.
87
* @param {?*} traverseContext Used to pass information throughout the traversal
88
* process.
89
* @return {!number} The number of children in this subtree.
90
*/
91
var traverseAllChildrenImpl =
92
function(children, nameSoFar, indexSoFar, callback, traverseContext) {
93
var nextName, nextIndex;
94
var subtreeCount = 0; // Count of children found in the current subtree.
95
if (Array.isArray(children)) {
96
for (var i = 0; i < children.length; i++) {
97
var child = children[i];
98
nextName = (
99
nameSoFar +
100
(nameSoFar ? SUBSEPARATOR : SEPARATOR) +
101
getComponentKey(child, i)
102
);
103
nextIndex = indexSoFar + subtreeCount;
104
subtreeCount += traverseAllChildrenImpl(
105
child,
106
nextName,
107
nextIndex,
108
callback,
109
traverseContext
110
);
111
}
112
} else {
113
var type = typeof children;
114
var isOnlyChild = nameSoFar === '';
115
// If it's the only child, treat the name as if it was wrapped in an array
116
// so that it's consistent if the number of children grows
117
var storageName =
118
isOnlyChild ? SEPARATOR + getComponentKey(children, 0) : nameSoFar;
119
if (children == null || type === 'boolean') {
120
// All of the above are perceived as null.
121
callback(traverseContext, null, storageName, indexSoFar);
122
subtreeCount = 1;
123
} else if (type === 'string' || type === 'number' ||
124
ReactElement.isValidElement(children)) {
125
callback(traverseContext, children, storageName, indexSoFar);
126
subtreeCount = 1;
127
} else if (type === 'object') {
128
invariant(
129
!children || children.nodeType !== 1,
130
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
131
'elements are not valid children of React components.'
132
);
133
for (var key in children) {
134
if (children.hasOwnProperty(key)) {
135
nextName = (
136
nameSoFar + (nameSoFar ? SUBSEPARATOR : SEPARATOR) +
137
wrapUserProvidedKey(key) + SUBSEPARATOR +
138
getComponentKey(children[key], 0)
139
);
140
nextIndex = indexSoFar + subtreeCount;
141
subtreeCount += traverseAllChildrenImpl(
142
children[key],
143
nextName,
144
nextIndex,
145
callback,
146
traverseContext
147
);
148
}
149
}
150
}
151
}
152
return subtreeCount;
153
};
154
155
/**
156
* Traverses children that are typically specified as `props.children`, but
157
* might also be specified through attributes:
158
*
159
* - `traverseAllChildren(this.props.children, ...)`
160
* - `traverseAllChildren(this.props.leftPanelChildren, ...)`
161
*
162
* The `traverseContext` is an optional argument that is passed through the
163
* entire traversal. It can be used to store accumulations or anything else that
164
* the callback might find relevant.
165
*
166
* @param {?*} children Children tree object.
167
* @param {!function} callback To invoke upon traversing each child.
168
* @param {?*} traverseContext Context for traversal.
169
* @return {!number} The number of children in this subtree.
170
*/
171
function traverseAllChildren(children, callback, traverseContext) {
172
if (children == null) {
173
return 0;
174
}
175
176
return traverseAllChildrenImpl(children, '', 0, callback, traverseContext);
177
}
178
179
module.exports = traverseAllChildren;
180
181