Children.map

  1. function mapChildren(children, func, context) {
  2. // 如果遍历的 children 为空则直接返回,包括 undefined
  3. if (children == null) {
  4. return children;
  5. }
  6. // 创建用于存储结果的数组,最后返回
  7. const result = [];
  8. mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  9. return result;
  10. }
  11. // 带着内部 key 值前缀进行映射?
  12. function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
  13. // 默认前缀
  14. let escapedPrefix = '';
  15. // 如果传入的前缀不为空,转义后存入 escapedPrefix
  16. if (prefix != null) {
  17. escapedPrefix = escapeUserProvidedKey(prefix) + '/';
  18. }
  19. const traverseContext = getPooledTraverseContext(
  20. array,
  21. escapedPrefix,
  22. func,
  23. context,
  24. );
  25. // 遍历所有 children
  26. traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
  27. releaseTraverseContext(traverseContext);
  28. }
  29. const POOL_SIZE = 10;
  30. const traverseContextPool = [];
  31. function getPooledTraverseContext(
  32. mapResult,
  33. keyPrefix,
  34. mapFunction,
  35. mapContext,
  36. ) {
  37. if (traverseContextPool.length) {
  38. // 如果上下文缓存中已经有内容
  39. const traverseContext = traverseContextPool.pop();
  40. traverseContext.result = mapResult;
  41. traverseContext.keyPrefix = keyPrefix;
  42. traverseContext.func = mapFunction;
  43. traverseContext.context = mapContext;
  44. traverseContext.count = 0;
  45. return traverseContext;
  46. } else {
  47. // 如果缓存中没有已存在的上下文则直接返回
  48. return {
  49. result: mapResult,
  50. keyPrefix: keyPrefix,
  51. func: mapFunction,
  52. context: mapContext,
  53. count: 0,
  54. };
  55. }
  56. }
  57. // 如果 children 为空,则直接返回
  58. function traverseAllChildren(children, callback, traverseContext) {
  59. if (children == null) {
  60. return 0;
  61. }
  62. return traverseAllChildrenImpl(children, '', callback, traverseContext);
  63. }
  64. function traverseAllChildrenImpl(
  65. children,
  66. nameSoFar,
  67. callback,
  68. traverseContext,
  69. ) {
  70. const type = typeof children;
  71. // undefined 和 boolean 回归至 null
  72. if (type === 'undefined' || type === 'boolean') {
  73. // All of the above are perceived as null.
  74. children = null;
  75. }
  76. // 是否调用回调函数
  77. let invokeCallback = false;
  78. // 如果 children 是无效值或者是单值或者是组件,则确认直接调用回调即可
  79. if (children === null) {
  80. invokeCallback = true;
  81. } else {
  82. switch (type) {
  83. case 'string':
  84. case 'number':
  85. invokeCallback = true;
  86. break;
  87. case 'object':
  88. switch (children.$$typeof) {
  89. case REACT_ELEMENT_TYPE:
  90. case REACT_PORTAL_TYPE:
  91. invokeCallback = true;
  92. }
  93. }
  94. }
  95. // 如果可以直接调用回调函数,则直接调用回调,并返回
  96. if (invokeCallback) {
  97. callback(
  98. traverseContext,
  99. children,
  100. // If it's the only child, treat the name as if it was wrapped in an array
  101. // so that it's consistent if the number of children grows.
  102. // 如果 nameSoFar === '',说明所遍历只是单个节点,所以
  103. nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
  104. );
  105. return 1;
  106. }
  107. let child;
  108. let nextName;
  109. let subtreeCount = 0; // Count of children found in the current subtree.
  110. const nextNamePrefix =
  111. nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
  112. // 如果 children 是数组,则遍历数组,
  113. if (Array.isArray(children)) {
  114. for (let i = 0; i < children.length; i++) {
  115. child = children[i];
  116. nextName = nextNamePrefix + getComponentKey(child, i);
  117. subtreeCount += traverseAllChildrenImpl(
  118. child,
  119. nextName,
  120. callback,
  121. traverseContext,
  122. );
  123. }
  124. } else {
  125. const iteratorFn = getIteratorFn(children);
  126. if (typeof iteratorFn === 'function') {
  127. const iterator = iteratorFn.call(children);
  128. let step;
  129. let ii = 0;
  130. while (!(step = iterator.next()).done) {
  131. child = step.value;
  132. nextName = nextNamePrefix + getComponentKey(child, ii++);
  133. subtreeCount += traverseAllChildrenImpl(
  134. child,
  135. nextName,
  136. callback,
  137. traverseContext,
  138. );
  139. }
  140. } else if (type === 'object') {
  141. let addendum = '';
  142. const childrenString = '' + children;
  143. invariant(
  144. false,
  145. 'Objects are not valid as a React child (found: %s).%s',
  146. childrenString === '[object Object]'
  147. ? 'object with keys {' + Object.keys(children).join(', ') + '}'
  148. : childrenString,
  149. addendum,
  150. );
  151. }
  152. }
  153. return subtreeCount;
  154. }
  155. function mapSingleChildIntoContext(bookKeeping, child, childKey) {
  156. const {result, keyPrefix, func, context} = bookKeeping;
  157. let mappedChild = func.call(context, child, bookKeeping.count++);
  158. if (Array.isArray(mappedChild)) {
  159. mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
  160. } else if (mappedChild != null) {
  161. if (isValidElement(mappedChild)) {
  162. mappedChild = cloneAndReplaceKey(
  163. mappedChild,
  164. // Keep both the (mapped) and old keys if they differ, just as
  165. // traverseAllChildren used to do for objects as children
  166. keyPrefix +
  167. (mappedChild.key && (!child || child.key !== mappedChild.key)
  168. ? escapeUserProvidedKey(mappedChild.key) + '/'
  169. : '') +
  170. childKey,
  171. );
  172. }
  173. result.push(mappedChild);
  174. }
  175. }

Children.forEach

  1. function forEachChildren(children, forEachFunc, forEachContext) {
  2. if (children == null) {
  3. return children;
  4. }
  5. const traverseContext = getPooledTraverseContext(
  6. null,
  7. null,
  8. forEachFunc,
  9. forEachContext,
  10. );
  11. traverseAllChildren(children, forEachSingleChild, traverseContext);
  12. releaseTraverseContext(traverseContext);
  13. }

Children.count

  1. function countChildren(children) {
  2. return traverseAllChildren(children, () => null, null);
  3. }

Children.only

  1. function onlyChild(children) {
  2. invariant(
  3. isValidElement(children),
  4. 'React.Children.only expected to receive a single React element child.',
  5. );
  6. return children;
  7. }

toArray

  1. function toArray(children) {
  2. const result = [];
  3. mapIntoWithKeyPrefixInternal(children, result, null, child => child);
  4. return result;
  5. }