ReactChildren

本文介绍 React 中 ReactChildren 模块的功能,包括遍历、映射、计数和转换子元素的方法,以及如何管理和释放内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ReactChildren模块用于处理props.children。props.children可以是数组(包含数组项为ReactNode数组的情形)或单个reactNode形式,

 

'use strict';

// PooledClass.addPoolingTo将某构造函数装饰为可存储实例池,getPooled创建实例,release销毁实例数据
var PooledClass = require('./PooledClass');
var ReactElement = require('./ReactElement');

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

// traverseAllChildren(children,callback,traverseContext)
// 对children的子孙节点执行callback,并传入参数traverseContext
// callback中,对children的子孙节点执行traverseContext.func方法
var traverseAllChildren = require('./traverseAllChildren');

var twoArgumentPooler = PooledClass.twoArgumentPooler;
var fourArgumentPooler = PooledClass.fourArgumentPooler;

var userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
  return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
}

// 参数forEachFunction为遍历元素时,以元素为参数的待执行函数
// 参数forEachContext为forEachFunction函数执行时的上下文
// 使用PooledClass.addPoolingTo将ForEachBookKeeping构造函数封装成可使用getPooled创建实例
function ForEachBookKeeping(forEachFunction, forEachContext) {
  this.func = forEachFunction;
  this.context = forEachContext;
  this.count = 0;
}
ForEachBookKeeping.prototype.destructor = function () {
  this.func = null;
  this.context = null;
  this.count = 0;
};
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);

// 参数bookKeeping为ForEachBookKeeping实例,实例中含有处理元素的执行函数及其上下文
function forEachSingleChild(bookKeeping, child, name) {
  var func = bookKeeping.func,
      context = bookKeeping.context;

  func.call(context, child, bookKeeping.count++);// bookKeeping.count++为children中子孙节点的总数
}

// 将用户配置的执行函数forEachFunc、和执行上下文forEachContext存入实例traverseContext中
// traverseAllChildren遍历children,调用forEachSingleChild对children中的每一项执行forEachFunc函数
// 最后调用release方法释放内存
function forEachChildren(children, forEachFunc, forEachContext) {
  if (children == null) {
    return children;
  }
  var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
  traverseAllChildren(children, forEachSingleChild, traverseContext);
  ForEachBookKeeping.release(traverseContext);
}

// this.result存储执行mapFunction函数后获取得到的ReactNode
// this.keyPrefix作为ReactNode的key值前缀
function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
  this.result = mapResult;
  this.keyPrefix = keyPrefix;
  this.func = mapFunction;
  this.context = mapContext;
  this.count = 0;
}
MapBookKeeping.prototype.destructor = function () {
  this.result = null;
  this.keyPrefix = null;
  this.func = null;
  this.context = null;
  this.count = 0;
};
PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
  var result = bookKeeping.result,
      keyPrefix = bookKeeping.keyPrefix,
      func = bookKeeping.func,
      context = bookKeeping.context;

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

  // 对ReactNode数据格式的child执行func函数后返回数组,将该数组项填入result中
  // 比如props.children挂载的ReactNode节点劫持渲染其props属性中的ReactNode
  if (Array.isArray(mappedChild)) {
    mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument);

  // 对ReactNode数据格式的child执行func函数后返回ReactNode,将该ReactNode推入result中
  // 并替换该ReactNode的key值为节点路径形式
  } else if (mappedChild != null) {
    if (ReactElement.isValidElement(mappedChild)) {
      mappedChild = ReactElement.cloneAndReplaceKey(mappedChild,
        keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) 
          ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey);
    }
    result.push(mappedChild);
  }
}

// 遍历props.children子孙元素执行func,将func的返回值推入array中
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
  var escapedPrefix = '';
  if (prefix != null) {
    escapedPrefix = escapeUserProvidedKey(prefix) + '/';
  }
  var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context);
  traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
  MapBookKeeping.release(traverseContext);
}

// 遍历children子孙元素执行func,将func的返回值推入result中,并返回result
function mapChildren(children, func, context) {
  if (children == null) {
    return children;
  }
  var result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  return result;
}

function forEachSingleChildDummy(traverseContext, child, name) {
  return null;
}

// 计算children中含有ReactNode的个数
function countChildren(children, context) {
  return traverseAllChildren(children, forEachSingleChildDummy, null);
}

// 将嵌套的子孙节点转化为单层数组形式,emptyFunction.thatReturnsArgument返回传参ReactNode
function toArray(children) {
  var result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument);
  return result;
}

var ReactChildren = {
  // ReactChildren.forEach(children,func,context)
  // 遍历props.children子孙节点,执行func函数
  forEach: forEachChildren,

  // ReactChildren.map(children,func,context)
  // 遍历props.children子孙节点,执行func函数,func返回值组装成数组形式的ReactNode,即result后输出
  // 与forEach不同的是,forEach只为执行func函数,map获取result
  map: mapChildren,

  // ReactChildren.mapIntoWithKeyPrefixInternal(children,array,prefix,func,context)
  // 遍历props.children子孙元素执行func,将func的返回值推入array中
  // 与map方法不同的是,可实现功能定制,比如实现count、toArray方法
  mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,

  // ReactChildren.countChildren(children,context)
  // 统计props.children含有ReactNode个数
  count: countChildren,

  // ReactChildren.toArray(children)
  // 将嵌套式的props.children转化为数组形式的ReactNode
  toArray: toArray
};

module.exports = ReactChildren;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值