One example,

function ChildrenDemo(props{

  console.log(props.children, 'children30');

  console.log(React.Children.map(props.children, item => [item, [item, [item]]]), 'children31');

  // console.log(React.Children.map(props.children,item=>item),'children31')

  return props.children;

}



export default() = > (

  <ChildrenDemo>

    <span key={'. 0/ '} >1</span>

    <span>2</span>

  </ChildrenDemo>


)

Copy the code

Props. Children:

React.children. Map (props. Children, item => [item, [item, [item]]] :

[item, [item, [item]] [item, [item]] [item,item,item]] [item,item,item]

Second, the React. Children. The map () function: zh-hans.reactjs.org/docs/react-…

Source:

// React.Children.map(props.children,item=>[item,[item,] ])

function mapChildren(children, func, context{

  if (children == null) {

    return children;

  }

  const result = [];

  // After basic judgment and initialization, call this method

  //props.children,[],null,(item)=>{return [item,[item,] ]},undefined

  mapIntoWithKeyPrefixInternal(children, result, null, func, context);

  return result;

}



export {

  //as is the name of mapChildren

  forEachChildren as forEach,

  mapChildren as map,

  countChildren as count,

  onlyChild as only,

  toArray,

};

Copy the code

If the array is rolled around, return the result

Third, mapIntoWithKeyPrefixInternal () function: getPooledTraverseContext ()/traverseAllChildren ()/releaseTraverseContext () wrapper

Source:

//First time: props. Children, [],null , (item)= >{return [item,[item,] ]} , undefined

//[item,[item,]],[],. 0 , c => c , undefined

function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {

  let escapedPrefix = ' ';

  //If there are more than one consecutive string/ is followed by a slash after the matching string

  if(prefix ! =null) {

    escapedPrefix = escapeUserProvidedKey(prefix) + '/';

  }

  //Find an object from the pool

  //[].' '.(item)= >{return [item,[item,] ]},undefined



  //traverseContext=

  // {

  //  result:[],

  //  keyPrefix:' '.

  //  func:(item)= >{return [item,[item,] ]},

  //  context:undefined.

  //  count:0.

  // }

  const traverseContext = getPooledTraverseContext(

    array,

    escapedPrefix,

    func,

    context,

  );

  //Flatten a nested set of numbers

  traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);

  releaseTraverseContext(traverseContext);

}

Copy the code

Resolution:

① The escapeUserProvidedKey() function is usually used for second-level recursion

What it does: Add a/after /

Source:

const userProvidedKeyEscapeRegex = /\/+/g;

function escapeUserProvidedKey(text{

  // If there are consecutive slashes in the string, the slash is followed by the matching string

  return (' ' + text).replace(userProvidedKeyEscapeRegex, '$& /');

}

Copy the code

React defines a rule for keys: if there are consecutive slashes in a string, add a slash after the matching string

Ex. :

let a='aa/a/'

console.log(a.replace(/\/+/g.'$& /')); // aa//a//

Copy the code

2.getPooledTraverseContext()

What it does: Creates a pool of objects and reuses objects, thereby reducing many of the memory and garbage collection costs associated with Object creation

Source:

// The maximum capacity of the object pool is 10

const POOL_SIZE = 10;

/ / object pool

const traverseContextPool = [];

//[],'',(item)=>{return [item,[item,] ]},undefined

function getPooledTraverseContext(

  mapResult,

  keyPrefix,

  mapFunction,

  mapContext,

{

  // If there are objects in the object pool, an object will be queued.

  // Assign arguments' value to the object property

  // Finally return the object

  if (traverseContextPool.length) {

    const traverseContext = traverseContextPool.pop();

    traverseContext.result = mapResult;

    traverseContext.keyPrefix = keyPrefix;

    traverseContext.func = mapFunction;

    traverseContext.context = mapContext;

    traverseContext.count = 0;

    return traverseContext;

  }

  // If not, a new object is returned

  else {

    / / {

    // result:[],

    // keyPrefix:'',

    // func:(item)=>{return [item,[item,] ]},

    // context:undefined,

    // count:0,

    // }

    return {

      result: mapResult,

      keyPrefix: keyPrefix,

      func: mapFunction,

      context: mapContext,

      count0.

    };

  }

}

Copy the code

Resolution: The traverseContext object is used for each recursion of a map(). The purpose of the traverseContextPool object pool is to reuse the objects in it to reduce memory consumption and initialize the reused objects at the end of the map(). And push it into the object pool (releaseTraverseContext) for the next map()

(3) mapSingleChildIntoContext () is mapSingleChildIntoContext traverseAllChildren (children, mapSingleChildIntoContext. The second parameter of traverseContext, in order to avoid talking about traverseAllChildren and looking at this API, let’s analyze it first

The recursion is still the child of the array; Add a single ReactElement child to Result

Source:

//bookKeeping:traverseContext=

/ / {

// result:[],

// keyPrefix:'',

// func:(item)=>{return [item,[item,] ]},

// context:undefined,

// count:0,

// }



//child:<span>1<span/>



//childKey:.0

function mapSingleChildIntoContext(bookKeeping, child, childKey{

  // Destruct the assignment

  const {result, keyPrefix, func, context} = bookKeeping;

  //func:(item)=>{return [item,[item,] ]},

  / / the item that is < span > 1 < / span >

  // the second argument, bookKeeping. Count++, is interesting and doesn't use it at all, but still counts

  let mappedChild = func.call(context, child, bookKeeping.count++);

  // If the react.children.map () second argument callback is still an array,

  / / recursive call mapIntoWithKeyPrefixInternal, continue the previous steps,

  // until a single ReactElement

  if (Array.isArray(mappedChild)) {

    //mappedChild:[item,[item,] ]

    //result:[]

    //childKey:.0

    //func:c => c

    mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);

  }

  // mappedChild is a single ReactElement and is not null

  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,

        // Keep both the (mapped) and old keys if they differ, just as

        // traverseAllChildren used to do for objects as children

        // If the old and new keys are different, keep both, as traverseAllChildren do with Objects

        keyPrefix +

(mappedChild.key && (! child || child.key ! == mappedChild.key)

            ? escapeUserProvidedKey(mappedChild.key) + '/'

            : ' ') +

          childKey,

      );

    }

    //result is the result of the map return

    result.push(mappedChild);

  }

}

Copy the code

(1) Let child call the func method and continue recursing if the result is an array. If it is a single ReactElement, place it in the Result array

(2) cloneAndReplaceKey() assigns the same attribute to the new object as its name, replacing the key attribute

Take a quick look at the source code:

export function cloneAndReplaceKey(oldElement, newKey{

  const newElement = ReactElement(

    oldElement.type,

    newKey,

    oldElement.ref,

    oldElement._self,

    oldElement._source,

    oldElement._owner,

    oldElement.props,

  );



  return newElement;

}

Copy the code

(3) isValidElement() check whether it is ReactElement.

export function isValidElement(object{

  return (

    typeof object= = ='object' &&

    object! = =null &&

    object.?typeof === REACT_ELEMENT_TYPE

  );

}

Copy the code

(4)traverseAllChildren()

Action: traverseAllChildrenImpl Trigger

Source:

// children, mapSingleChildIntoContext, traverseContext

function traverseAllChildren(children, callback, traverseContext{

  if (children == null) {

    return 0;

  }



  return traverseAllChildrenImpl(children, ' ', callback, traverseContext);

}

Copy the code

5.traverseAllChildrenImpl()

What it does: A core recursive function that flattens a nested array

Source:

// children, '', mapSingleChildIntoContext, traverseContext

function traverseAllChildrenImpl(

  children,

  nameSoFar,

  callback,

  //traverseContext=

  // {

  //  result:[],

  //  keyPrefix:' '.

  //  func:(item
) = >
{return [item,[item,] ]},

  // context:undefined,

  // count:0,

  // }

  traverseContext,

) {

  const type = typeof children;



  if (type === 'undefined' || type === 'boolean') {

    // All of the above are considered null

    // All of the above are perceived as null.

    children = null;

  }

  // Call func flag

  let invokeCallback = false;



  if (children === null) {

    invokeCallback = true;

  } else {

    switch (type) {

      case 'string':

      case 'number':

        invokeCallback = true;

        break;

      case 'object':

        / / if props. The children is a single ReactElement/PortalElement

        When traverseAllChildrenImpl, 1 and 2 are child

        // Must trigger invokeCallback=true

        switch (children.?typeof) {

          case REACT_ELEMENT_TYPE:

          case REACT_PORTAL_TYPE:

            invokeCallback = true;

        }

    }

  }



  if (invokeCallback) {

    callback(

      traverseContext,

      children,

      // If there is only one child node, put it in an array

      // 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.

      / / $= 0



      //<span>1<span/> key='.0'

      nameSoFar === ' ' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,

    );

    return 1;

  }



  let child;

  let nextName;

  // How many children are there

  let subtreeCount = 0// Count of children found in the current subtree.

  const nextNamePrefix =

    / /.

    nameSoFar === ' ' ? SEPARATOR : nameSoFar + SUBSEPARATOR;



  if (Array.isArray(children)) {

    for (let i = 0; i < children.length; i++) {

      //<span>1</span>

      child = children[i];

      // If the key is not set manually, the first layer is.0 and the second layer is.1

      nextName = nextNamePrefix + getComponentKey(child, i);



      subtreeCount += traverseAllChildrenImpl(

        child,

        nextName,

        callback,

        traverseContext,

      );

    }

  } else {

    const iteratorFn = getIteratorFn(children);

    if (typeof iteratorFn === 'function') {

      if (__DEV__) {

        // Warn about using Maps as children

        if (iteratorFn === children.entries) {

          warning(

            didWarnAboutMaps,

            'Using Maps as children is unsupported and will likely yield ' +

              'unexpected results. Convert it to a sequence/iterable of keyed ' +

              'ReactElements instead.'.

          );

          didWarnAboutMaps = true;

        }

      }



      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,

        );

      }

    }

    Throw Error if it is a pure object

    else if (type === 'object') {

      let addendum = ' ';

      if (__DEV__) {

        addendum =

          ' If you meant to render a collection of children, use an array ' +

          'instead.' +

          ReactDebugCurrentFrame.getStackAddendum();

      }

      const 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;

}

Copy the code

(1) Are children objects? Typeof is REACT_ELEMENT_TYPE/REACT_PORTAL_TYPE

Call the callback mapSingleChildIntoContext, copy in addition to the key attributes, replace the key attribute, puts it into the result

(2) Children is an Array of children and traverseAllChildrenImpl

Iii. Flow chart

4. Write an interview question based on the react.children. Map () algorithm

Array flattening: Implement a flatten method that outputs a flattened array if you input an array of elements

// Example

let givenArr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, 11, 12, [12, 13, [14]]]], 10];

let outputArr = [1.2.2.3.4.5.5.6.7.8.9.11.12.12.13.14.10]

Flatten (givenArr) -- >outputArr

Copy the code

Solution 1: Use recursion according to the flow chart above

function flatten(arr){

    var res = [];

    for(var i=0; i<arr.length; i++){

        if(Array.isArray(arr[i])){

            res = res.concat(flatten(arr[i]));

        }else{

            res.push(arr[i]);

        }

    }

    return res;

}

Copy the code

Solution 2: ES6

function flatten(array) {

      // Merge as long as the elements in the array have a nested array

      while(array.some(item=>Array.isArray(item)))

        array=[].concat(...array)



      console.log(array/ /,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10 [1]

      return array

    }

Copy the code

(after)