diff --git a/src/utils/ReactChildren.js b/src/utils/ReactChildren.js index 49ca0ea2e0..f92e5da296 100644 --- a/src/utils/ReactChildren.js +++ b/src/utils/ReactChildren.js @@ -129,9 +129,25 @@ function mapChildren(children, func, context) { return mapResult; } +/** +* Count the number of children that are typically specified as +* `props.children`. +*/ + +function forEachSingleChildDummy(traverseContext, child, name, i) { + return null; +} + +function countChildren(children, context) { + var numberOfChildren = traverseAllChildren(children, forEachSingleChildDummy, + null); + return numberOfChildren; +} + var ReactChildren = { forEach: forEachChildren, - map: mapChildren + map: mapChildren, + count: countChildren }; module.exports = ReactChildren; diff --git a/src/utils/__tests__/ReactChildren-test.js b/src/utils/__tests__/ReactChildren-test.js index 4cfd192ed6..18f472a7ba 100644 --- a/src/utils/__tests__/ReactChildren-test.js +++ b/src/utils/__tests__/ReactChildren-test.js @@ -335,4 +335,67 @@ describe('ReactChildren', function() { expect(console.warn.calls.length).toEqual(1); expect(mapped).toEqual({'.$something': zero}); }); + + it('should return 0 for null children', function() { + var numberOfChildren = ReactChildren.count(null); + expect(numberOfChildren).toBe(0); + }); + + it('should return 0 for undefined children', function() { + var numberOfChildren = ReactChildren.count(undefined); + expect(numberOfChildren).toBe(0); + }); + + it('should return 1 for single child', function() { + var simpleKid = ; + var instance =
{simpleKid}
; + var numberOfChildren = ReactChildren.count(instance.props.children); + expect(numberOfChildren).toBe(1); + }); + + it('should count the number of children in flat structure', function() { + var zero =
; + var one = null; + var two =
; + var three = null; + var four =
; + + var instance = ( +
+ {zero} + {one} + {two} + {three} + {four} +
+ ); + var numberOfChildren = ReactChildren.count(instance.props.children); + expect(numberOfChildren).toBe(5); + }); + + it('should count the number of children in nested structure', function() { + var zero =
; + var one = null; + var two =
; + var three = null; + var four =
; + var five =
; + // five is placed into a JS object with a key that is joined to the + // component key attribute. + // Precedence is as follows: + // 1. If grouped in an Object, the object key combined with `key` prop + // 2. If grouped in an Array, the `key` prop, falling back to array index + + var instance = ( +
{ + [{ + firstHalfKey: [zero, one, two], + secondHalfKey: [three, four], + keyFive: five + }] + }
+ ); + var numberOfChildren = ReactChildren.count(instance.props.children); + expect(numberOfChildren).toBe(6); + }); }); diff --git a/src/utils/traverseAllChildren.js b/src/utils/traverseAllChildren.js index d89d4402bc..624b70c473 100644 --- a/src/utils/traverseAllChildren.js +++ b/src/utils/traverseAllChildren.js @@ -180,10 +180,14 @@ var traverseAllChildrenImpl = * @param {?*} children Children tree object. * @param {!function} callback To invoke upon traversing each child. * @param {?*} traverseContext Context for traversal. + * @return {!number} The number of children in this subtree. */ function traverseAllChildren(children, callback, traverseContext) { if (children !== null && children !== undefined) { - traverseAllChildrenImpl(children, '', 0, callback, traverseContext); + return traverseAllChildrenImpl(children, '', 0, callback, traverseContext); + } + else { + return 0; } }