Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move traverseAllChildren into ReactChildren
  • Loading branch information
koba04 committed Jun 29, 2017
commit efb71072129b2a9eff9eb4bc5c914d9c0b1abd0f
168 changes: 164 additions & 4 deletions src/isomorphic/children/ReactChildren.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,161 @@
var ReactElement = require('ReactElement');

var emptyFunction = require('fbjs/lib/emptyFunction');
var traverseAllChildren = require('traverseAllChildren');

var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
var REACT_ELEMENT_TYPE = require('ReactElementSymbol');

var getIteratorFn = require('getIteratorFn');
var invariant = require('fbjs/lib/invariant');
var KeyEscapeUtils = require('KeyEscapeUtils');
var warning = require('fbjs/lib/warning');

var SEPARATOR = '.';
var SUBSEPARATOR = ':';

/**
* This is inlined from ReactElement since this file is shared between
* isomorphic and renderers. We could extract this to a
*
*/

/**
* TODO: Test that a single child and an array with one item have the same key
* pattern.
*/

var didWarnAboutMaps = false;

/**
* Generate a key string that identifies a component within a set.
*
* @param {*} component A component that could contain a manual key.
* @param {number} index Index that is used if a manual key is not provided.
* @return {string}
*/
function getComponentKey(component, index) {
// Do some typechecking here since we call this blindly. We want to ensure
// that we don't block potential future ES APIs.
if (component && typeof component === 'object' && component.key != null) {
// Explicit key
return KeyEscapeUtils.escape(component.key);
}
// Implicit key determined by the index in the set
return index.toString(36);
}

function traverseAllChildren(
children,
nameSoFar,
callback,
traverseContext,
) {
var type = typeof children;

if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}

if (
children === null ||
type === 'string' ||
type === 'number' ||
// The following is inlined from ReactElement. This means we can optimize
// some checks. React Fiber also inlines this logic for similar purposes.
(type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
) {
callback(
traverseContext,
children,
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows.
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
);
return 1;
}

var child;
var nextName;
var subtreeCount = 0; // Count of children found in the current subtree.
var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;

if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);
subtreeCount += traverseAllChildren(
child,
nextName,
callback,
traverseContext,
);
}
} else {
var iteratorFn = getIteratorFn(children);
if (iteratorFn) {
if (__DEV__) {
// Warn about using Maps as children
if (iteratorFn === children.entries) {
let mapsAsChildrenAddendum = '';
if (ReactCurrentOwner.current) {
var mapsAsChildrenOwnerName = ReactCurrentOwner.current.getName();
if (mapsAsChildrenOwnerName) {
mapsAsChildrenAddendum = '\n\nCheck the render method of `' +
mapsAsChildrenOwnerName +
'`.';
}
}
warning(
didWarnAboutMaps,
'Using Maps as children is unsupported and will likely yield ' +
'unexpected results. Convert it to a sequence/iterable of keyed ' +
'ReactElements instead.%s',
mapsAsChildrenAddendum,
);
didWarnAboutMaps = true;
}
}

var iterator = iteratorFn.call(children);
var step;
var ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
nextName = nextNamePrefix + getComponentKey(child, ii++);
subtreeCount += traverseAllChildren(
child,
nextName,
callback,
traverseContext,
);
}
} else if (type === 'object') {
var addendum = '';
if (__DEV__) {
addendum = ' If you meant to render a collection of children, use an array ' +
'instead.';
if (ReactCurrentOwner.current) {
var name = ReactCurrentOwner.current.getName();
if (name) {
addendum += '\n\nCheck the render method of `' + name + '`.';
}
}
}
var childrenString = '' + children;
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,
);
}
}

return subtreeCount;
}

var userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
Expand Down Expand Up @@ -47,7 +201,7 @@ function forEachChildren(children, forEachFunc, forEachContext) {
context: forEachContext,
count: 0,
};
traverseAllChildren(children, forEachSingleChild, traverseContext);
traverseAllChildren(children, '', forEachSingleChild, traverseContext);
}

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
Expand Down Expand Up @@ -79,6 +233,9 @@ function mapSingleChildIntoContext(bookKeeping, child, childKey) {
}

function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
if (children == null) {
return;
}
var escapedPrefix = '';
if (prefix != null) {
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
Expand All @@ -91,7 +248,7 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
count: 0,
};

traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
traverseAllChildren(children, '', mapSingleChildIntoContext, traverseContext);
}

/**
Expand Down Expand Up @@ -130,7 +287,10 @@ function forEachSingleChildDummy(traverseContext, child, name) {
* @return {number} The number of children.
*/
function countChildren(children, context) {
return traverseAllChildren(children, forEachSingleChildDummy, null);
if (children == null) {
return 0;
}
return traverseAllChildren(children, '', forEachSingleChildDummy, null);
}

/**
Expand Down