react 源码之React.children.map()

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>
)

它的输出是:

[object Array]: [Object, Object, Object, Object, Object, Object]

0: Object
: Object
$$typeof: <No implicit conversion of Symbol to String>

_owner: Object

_self: Object

_source: Object

_store: Object
key: ".$.0///.$.0/"

props: Object
ref: null
type: "span"

__proto__: Object

1: Object

2: Object

3: Object

4: Object

5: Object
length: 6

由于 csdn图片上传有bug。。我就这么大概写一写。

map的功能呢,是将数组一层一层展开,并赋予不同的key值;

二、React.Children.map()

源码:

// React.Children.map(props.children,item=>[item,[item,] ])
function mapChildren(children, func, context) {
  if (children == null) {
    return children;
  }
  const result = [];
  //进行基本的判断和初始化后,调用该方法
  //props.children,[],null,(item)=>{return [item,[item,] ]},undefined
  mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  return result;
}

export {
  //as就是重命名了,map即mapChildren
  forEachChildren as forEach,
  mapChildren as map,
  countChildren as count,
  onlyChild as only,
  toArray,
};

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

children在mapIntoWithKeyPrefixInternal(children, result, null, func, context); 运行一圈以后,就返回result

 

三、mapIntoWithKeyPrefixInternal()

//第一次:props.children , [] , null , (item)=>{return [item,[item,] ]} , undefined
//第二次:[item,[item,] ] , [] , .0 , c => c , undefined
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
  let escapedPrefix = '';
  //如果字符串中有连续多个 / 的话,在匹配的字串后再加 /
  if (prefix != null) {
    escapedPrefix = escapeUserProvidedKey(prefix) + '/';
  }
  //从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,
  );
  //将嵌套的数组展平
  traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
  releaseTraverseContext(traverseContext);//traverseContext对象里面的属性值设置为null
}

 四、traverseAllChildren

function traverseAllChildren(children, callback, traverseContext) {
  if (children == null) {
    return 0;
  }

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

function traverseAllChildrenImpl(
  children,
  nameSoFar,
  callback,
  traverseContext,
) {

  const type = typeof children;

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

  let invokeCallback = false;

  if (children === null) {
    invokeCallback = true;
  } else {
    switch (type) {
      case 'string'://有可能是VText;
      case 'number':
        invokeCallback = true;
        break;
      case 'object'://如果是对象
        switch (children.$$typeof) {
          case REACT_ELEMENT_TYPE://对象是普通组件
          case REACT_PORTAL_TYPE://对象是Protals组件;
            invokeCallback = true;
        }
    }
  }

  if (invokeCallback) {
    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;
  }

  let child;
  let nextName;
  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++) {
      child = children[i];
      nextName = nextNamePrefix + getComponentKey(child, i);
      subtreeCount += traverseAllChildrenImpl(//再调用一次该函数;subtreeCount不重要;
        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,
        );
      }
    } 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;
}

解析

 上面的函数是将组件元素或者Vtext元素放入回调函数中滚一圈。如果是数组,那么递归它的子元素,直到它的子元素是组件元素或者Vtext元素为止;

 

 

五、mapSingleChildIntoContext 

上面的callback就是mapSingleChildIntoContext

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
  const {result, keyPrefix, func, context} = bookKeeping;

  let mappedChild = func.call(context, child, bookKeeping.count++);
  if (Array.isArray(mappedChild)) {
    mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
  } else if (mappedChild != null) {
    if (isValidElement(mappedChild)) {
      mappedChild = cloneAndReplaceKey(
        mappedChild,
        // Keep both the (mapped) and old keys if they differ, just as
        // traverseAllChildren used to do for objects as children
        keyPrefix +
          (mappedChild.key && (!child || child.key !== mappedChild.key)
            ? escapeUserProvidedKey(mappedChild.key) + '/'
            : '') +
          childKey,
      );
    }
    result.push(mappedChild);//放入result数组;
  }
}

 以上就是核心的思路。

 

零星的方法:

六、getPooledTraverseContext

/对象池的最大容量为10
const POOL_SIZE = 10;
//对象池
const traverseContextPool = [];
//[],'',(item)=>{return [item,[item,] ]},undefined
function getPooledTraverseContext(
  mapResult,
  keyPrefix,
  mapFunction,
  mapContext,
) {
  //如果对象池内存在对象,则出队一个对象,
  //并将arguments的值赋给对象属性
  //最后返回该对象
  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 {
    //{
    // result:[],
    // keyPrefix:'',
    // func:(item)=>{return [item,[item,] ]},
    // context:undefined,
    // count:0,
    // }
    return {
      result: mapResult,
      keyPrefix: keyPrefix,
      func: mapFunction,
      context: mapContext,
      count: 0,
    };
  }
}

将  mapResult,keyPrefix,mapFunction,mapContext,放进同一个对象;

在每次map()的过程中,每次递归都会用到traverseContext对象,
创建traverseContextPool对象池的目的,就是复用里面的对象,
以减少内存消耗
,并且在map()结束时,
将复用的对象初始化,并push进对象池中(releaseTraverseContext),以供下次map()时使用

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值