UNPKG

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