This method is less commonly used. Ant Design Tabs uses this method to obtain child components, copy child components, and add new properties to generate a new component.
Let’s start with an example
class Child extends Component {
componentDidMount() {
console.log(
React.Children.map(this.props.children, item => {
return[item, [item]]; })); } render() {return (
<div>
{React.Children.map(this.props.children, item => [
item,
[item]
])}
</div>); }}class ChildrenMap extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Child>
<div>1</div>
<div>2</div>
</Child>); }}export default ChildrenMap;
Copy the code
Will be generated
The printed data structure is as follows
[item, [item]] [item, props. Children] [item,item] [React.Children.
Here we combine the source code to see how to implement the process:
A, mapChildren
// Pass the children raw data, func method, and context are rarely ignored.
function mapChildren(children, func, context) {
if (children == null) {
return children;
}
const result = [];
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}
Copy the code
Returns result as a new array
Second, the mapIntoWithKeyPrefixInternal
//children primitive array, array array to return, prefix: array key to generate, func: primitive method
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
let escapedPrefix = ' ';
if(prefix ! =null) {
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
}
// Go to the object pool to get data
const traverseContext = getPooledTraverseContext(
array,
escapedPrefix,
func,
context,
);
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
// Clear the object pool
releaseTraverseContext(traverseContext);
}
Copy the code
The main function of the escapeUserProvidedKey is to generate the key after the second level of recursion.
const POOL_SIZE = 10;
const traverseContextPool = [];
// Obtain the maximum number of objects from the object pool, which can reduce the number of object generation, to achieve performance optimization. You can just go to the object pool and get it when you need it.
// Mainly because traverseContext objects are generated multiple times during recursion.
function getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext,) {
if (traverseContextPool.length) {
const traverseContext = traverseContextPool.pop();
traverseContext.result = mapResult;
traverseContext.keyPrefix = keyPrefix;
traverseContext.func = mapFunction;
traverseContext.context = mapContext;
traverseContext.count = 0;
return traverseContext;
} else {
return {
result: mapResult,
keyPrefix: keyPrefix,
func: mapFunction,
context: mapContext,
count: 0}; }}Copy the code
traverseAllChildrenImpl
TraverseAllChildren simply calls the traverseAllChildrenImpl method.
/ / the children is the original data, the callback is traverseAllChildren (children, mapSingleChildIntoContext traverseContext); Method of mapSingleChildIntoContext me for a while looking at the function traverseAllChildrenImpl (children, nameSoFar, callback, traverseContext ) { const type = typeof children; if (type === 'undefined' || type === 'boolean') { children = null; } let invokeCallback = false; if (children === null) { invokeCallback = true; } else { switch (type) { case 'string': case 'number': invokeCallback = true; break; case 'object': switch (children.? typeof) { case REACT_ELEMENT_TYPE: case REACT_PORTAL_TYPE: invokeCallback = true; }}} the if (invokeCallback) {/ / callback mapSingleChildIntoContext callback this method (traverseContext, children, nameSoFar = = = ' '? SEPARATOR + getComponentKey(children, 0) : nameSoFar, ); return 1; } let child; let nextName; // Let subtreeCount = 0; / const nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR; if (Array.isArray(children)) { for (let i = 0; i < children.length; i++) { child = children[i]; NextName = nextNamePrefix + getComponentKey(child, I); SubtreeCount += traverseAllChildrenImpl(Child, nextName, callback, traverseContext); subtreeCount += traverseAllChildrenImpl(Child, nextName, callback, traverseContext,); }} else {// Const iteratorFn = getIteratorFn(children); if (typeof iteratorFn === 'function') { if (disableMapsAsChildren) { } const iterator = iteratorFn.call(children); let step; let ii = 0; while (! (step = iterator.next()).done) { child = step.value; nextName = nextNamePrefix + getComponentKey(child, ii++); subtreeCount += traverseAllChildrenImpl( child, nextName, callback, traverseContext, ); } } else if (type === 'object') { let addendum = ''; const childrenString = '' + children; } } return subtreeCount; }Copy the code
The current traverseAllChildrenImpl method iterated through this.props. Children to determine if the current Item is an array, an array, or some other type, and if it is an array, an array recursively. If it performs mapSingleChildIntoContext method.
Four, mapSingleChildIntoContext
//traverseContext is the object data stored in the object pool
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
const {result, keyPrefix, func, context} = bookKeeping;
// Execute your own func method, step by step
let mappedChild = func.call(context, child, bookKeeping.count++);
if (Array.isArray(mappedChild)) {
// If the react.children.map () second argument callback is still an array,
/ / recursive call mapIntoWithKeyPrefixInternal, continue the previous steps,
// until a single ReactElement
// c => c returns the current node
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
} else if(mappedChild ! =null) {
if (isValidElement(mappedChild)) {
// Assign the same attribute to the new object except for key, replacing the key attribute
mappedChild = cloneAndReplaceKey(
mappedChild,
// If the old and new keys are different, keep both, as traverseAllChildren do with ObjectskeyPrefix + (mappedChild.key && (! child || child.key ! == mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) +'/'
: ' ') +
childKey,
);
}
//result is the result of the map returnresult.push(mappedChild); }}Copy the code
This is the heart of it.
- In the case that the current item is not an array, the func method is executed to obtain the item component that needs to be displayed or processed. CloneAndReplaceKey is also used to copy the DOM of the current item and generate a new key for the current item.
- If the array is a recursive call mapIntoWithKeyPrefixInternal method. Note that fuc is passed in c => c, and the original func is not passed in to prevent an endless loop.
Can finish all of this method to the interpretation, mainly using the recursive twice, traverseAllChildrenImpl and mapSingleChildIntoContext. TraverseAllChildrenImpl is to obtain the initial array and array generated mapSingleChildIntoContext recursion, MapSingleChildIntoContext is by returning the item component performs func method generates new components or array to a recursive call mapIntoWithKeyPrefixInternal method.