UNPKG

6.91 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 _prodInvariant = require('./reactProdInvariant');
12
13var ReactCurrentOwner = require('./ReactCurrentOwner');
14var REACT_ELEMENT_TYPE = require('./ReactElementSymbol');
15
16var getIteratorFn = require('./getIteratorFn');
17var invariant = require('fbjs/lib/invariant');
18var KeyEscapeUtils = require('./KeyEscapeUtils');
19var warning = require('fbjs/lib/warning');
20
21var SEPARATOR = '.';
22var SUBSEPARATOR = ':';
23
24/**
25 * This is inlined from ReactElement since this file is shared between
26 * isomorphic and renderers. We could extract this to a
27 *
28 */
29
30/**
31 * TODO: Test that a single child and an array with one item have the same key
32 * pattern.
33 */
34
35var didWarnAboutMaps = false;
36
37/**
38 * Generate a key string that identifies a component within a set.
39 *
40 * @param {*} component A component that could contain a manual key.
41 * @param {number} index Index that is used if a manual key is not provided.
42 * @return {string}
43 */
44function getComponentKey(component, index) {
45 // Do some typechecking here since we call this blindly. We want to ensure
46 // that we don't block potential future ES APIs.
47 if (component && typeof component === 'object' && component.key != null) {
48 // Explicit key
49 return KeyEscapeUtils.escape(component.key);
50 }
51 // Implicit key determined by the index in the set
52 return index.toString(36);
53}
54
55/**
56 * @param {?*} children Children tree container.
57 * @param {!string} nameSoFar Name of the key path so far.
58 * @param {!function} callback Callback to invoke with each child found.
59 * @param {?*} traverseContext Used to pass information throughout the traversal
60 * process.
61 * @return {!number} The number of children in this subtree.
62 */
63function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
64 var type = typeof children;
65
66 if (type === 'undefined' || type === 'boolean') {
67 // All of the above are perceived as null.
68 children = null;
69 }
70
71 if (children === null || type === 'string' || type === 'number' ||
72 // The following is inlined from ReactElement. This means we can optimize
73 // some checks. React Fiber also inlines this logic for similar purposes.
74 type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE) {
75 callback(traverseContext, children,
76 // If it's the only child, treat the name as if it was wrapped in an array
77 // so that it's consistent if the number of children grows.
78 nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
79 return 1;
80 }
81
82 var child;
83 var nextName;
84 var subtreeCount = 0; // Count of children found in the current subtree.
85 var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
86
87 if (Array.isArray(children)) {
88 for (var i = 0; i < children.length; i++) {
89 child = children[i];
90 nextName = nextNamePrefix + getComponentKey(child, i);
91 subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
92 }
93 } else {
94 var iteratorFn = getIteratorFn(children);
95 if (iteratorFn) {
96 var iterator = iteratorFn.call(children);
97 var step;
98 if (iteratorFn !== children.entries) {
99 var ii = 0;
100 while (!(step = iterator.next()).done) {
101 child = step.value;
102 nextName = nextNamePrefix + getComponentKey(child, ii++);
103 subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
104 }
105 } else {
106 if (process.env.NODE_ENV !== 'production') {
107 var mapsAsChildrenAddendum = '';
108 if (ReactCurrentOwner.current) {
109 var mapsAsChildrenOwnerName = ReactCurrentOwner.current.getName();
110 if (mapsAsChildrenOwnerName) {
111 mapsAsChildrenAddendum = ' Check the render method of `' + mapsAsChildrenOwnerName + '`.';
112 }
113 }
114 process.env.NODE_ENV !== 'production' ? warning(didWarnAboutMaps, 'Using Maps as children is not yet fully supported. It is an ' + 'experimental feature that might be removed. Convert it to a ' + 'sequence / iterable of keyed ReactElements instead.%s', mapsAsChildrenAddendum) : void 0;
115 didWarnAboutMaps = true;
116 }
117 // Iterator will provide entry [k,v] tuples rather than values.
118 while (!(step = iterator.next()).done) {
119 var entry = step.value;
120 if (entry) {
121 child = entry[1];
122 nextName = nextNamePrefix + KeyEscapeUtils.escape(entry[0]) + SUBSEPARATOR + getComponentKey(child, 0);
123 subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
124 }
125 }
126 }
127 } else if (type === 'object') {
128 var addendum = '';
129 if (process.env.NODE_ENV !== 'production') {
130 addendum = ' If you meant to render a collection of children, use an array ' + 'instead or wrap the object using createFragment(object) from the ' + 'React add-ons.';
131 if (children._isReactElement) {
132 addendum = " It looks like you're using an element created by a different " + 'version of React. Make sure to use only one copy of React.';
133 }
134 if (ReactCurrentOwner.current) {
135 var name = ReactCurrentOwner.current.getName();
136 if (name) {
137 addendum += ' Check the render method of `' + name + '`.';
138 }
139 }
140 }
141 var childrenString = String(children);
142 !false ? process.env.NODE_ENV !== 'production' ? 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) : _prodInvariant('31', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum) : void 0;
143 }
144 }
145
146 return subtreeCount;
147}
148
149/**
150 * Traverses children that are typically specified as `props.children`, but
151 * might also be specified through attributes:
152 *
153 * - `traverseAllChildren(this.props.children, ...)`
154 * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
155 *
156 * The `traverseContext` is an optional argument that is passed through the
157 * entire traversal. It can be used to store accumulations or anything else that
158 * the callback might find relevant.
159 *
160 * @param {?*} children Children tree object.
161 * @param {!function} callback To invoke upon traversing each child.
162 * @param {?*} traverseContext Context for traversal.
163 * @return {!number} The number of children in this subtree.
164 */
165function traverseAllChildren(children, callback, traverseContext) {
166 if (children == null) {
167 return 0;
168 }
169
170 return traverseAllChildrenImpl(children, '', callback, traverseContext);
171}
172
173module.exports = traverseAllChildren;
\No newline at end of file