React 源码学习01 ---- React.Children.map 的实现与应用

server/2024/11/15 7:06:39/

1. 使用方法

React.Children.map(children, function[(thisArg)])

2. 方法解释

在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg。如果 children 是一个数组,它将被遍历并为数组中的每个子节点调用该函数。如果子节点为 null 或是 undefined,则此方法将返回 null 或是 undefined,而不会返回数组。

3. 注意

如果 children 是一个 Fragment 对象,它将被视为单一子节点的情况处理,而不会被遍历。

4. React.Children.map 源码

4.1 mapChildren 源码
\packages\react\src\ReactChildren.jsfunction mapChildren(children: ?ReactNodeList,func: MapFunc,context: mixed,
): ?Array<React$Node> {// 判断传入的子元素列表是否是 null 或 undefined,条件成立,直接返回 childrenif (children == null) {// $FlowFixMe limitation refining abstract types in Flowreturn children;}// 存储处理后的 child 列表const result: Array<React$Node> = [];// 每一个 child 的索引let count = 0;// 将 children 中的每个元素映射到数组 result 中,并在映射过程中应用 func 函数mapIntoArray(children, result, '', '', function (child) {// 调用映射函数处理 child,并返回处理结果return func.call(context, child, count++);});return result;
}
4.2 mapChildren 函数解释
  1. children 是任意类型的子元素列表。
  2. func 操作子元素的映射函数,接受两个参数:子元素和索引,并返回一个映射后的结果。
  3. context 执行 func 映射函数的上下文。
  4. 检查 children 是否为 null 或 undefined,如果是,则直接返回 children。
  5. 创建一个名为 result 的空数组,用于存储处理后的子元素。
  6. 初始化一个计数器 count,用于记录当前处理的子元素的索引。
  7. 使用 mapIntoArray 函数遍历 children,并将每个子元素转换为数组形式存储在 result 中。mapIntoArray 函数还接受一个回调函数,该回调函数会调用 func 函数,并传入当前子元素和计数器的值。
  8. 返回处理后的 result 数组。
4.3 mapIntoArray 函数的实现
function mapIntoArray(children: ?ReactNodeList,array: Array<React$Node>,escapedPrefix: string,nameSoFar: string,callback: (?React$Node) => ?ReactNodeList,
): number {// 使用 typeof 获取 children 的类型const type = typeof children;// 判断 type 是否是 undefined 或者 boolean,跳转成立,将 children 赋值 nullif (type === 'undefined' || type === 'boolean') {// All of the above are perceived as null.children = null;}// 是否调用回调函数标记let invokeCallback = false;// 如果 children 是 null,则调用回调if (children === null) {invokeCallback = true;} else {// 判断 type 是 bigint、string、number 直接调用回调switch (type) {case 'bigint':case 'string':case 'number':invokeCallback = true;break;case 'object':// 如果 type 是 object,继续判断 $$typeof 的值switch ((children: any).$$typeof) {// 如果 $$typeof 是 REACT_ELEMENT_TYPE、REACT_PORTAL_TYPE直接进入回调case REACT_ELEMENT_TYPE:case REACT_PORTAL_TYPE:invokeCallback = true;break;// 如果 $$typeof 是 REACT_LAZY_TYPE 进行递归调用case REACT_LAZY_TYPE:const payload = (children: any)._payload;const init = (children: any)._init;return mapIntoArray(init(payload),array,escapedPrefix,nameSoFar,callback,);}}}// 如果 invokeCallback 是 true,直接调用回调if (invokeCallback) {const child = children;// 使用回调函数处理 child,然后赋值给 mappedChildlet mappedChild = callback(child);// 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:// 获取子元素的key,如果 nameSoFar 是空,直接使用 SEPARATOR + getElementKey(child, 0) 否则使用 nameSoFarconst childKey =nameSoFar === '' ? SEPARATOR + getElementKey(child, 0) : nameSoFar;// 判断处理后的子元素是否是数组【Array.isArray】 if (isArray(mappedChild)) {let escapedChildKey = '';// 如果 childKey 不为 null,则将 childKey 的单/替换为//,结果赋值给 escapedChildKeyif (childKey != null) {escapedChildKey = escapeUserProvidedKey(childKey) + '/';}// 如果是数组,递归调用 mapIntoArraymapIntoArray(mappedChild, array, escapedChildKey, '', c => c);} else if (mappedChild != null) {// 如果是 react 元素if (isValidElement(mappedChild)) {// 复制一份新的子元素const newChild = cloneAndReplaceKey(mappedChild,// Keep both the (mapped) and old keys if they differ, just as// traverseAllChildren used to do for objects as childrenescapedPrefix +// $FlowFixMe[incompatible-type] Flow incorrectly thinks React.Portal doesn't have a key(mappedChild.key != null &&(!child || child.key !== mappedChild.key)? escapeUserProvidedKey(// $FlowFixMe[unsafe-addition]'' + mappedChild.key, // eslint-disable-line react-internal/safe-string-coercion) + '/': '') +childKey,);mappedChild = newChild;}// 将子元素放入存储的数组中array.push(mappedChild);}return 1;}let child;let nextName;let subtreeCount = 0; // Count of children found in the current subtree.const nextNamePrefix =nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;// 如果 invokeCallback 为 false,// children 是否为数组if (isArray(children)) {// 遍历 children 中每个元素,调用 mapIntoArray 函数,并将返回的子树计数累加到 subtreeCount 中for (let i = 0; i < children.length; i++) {child = children[i];nextName = nextNamePrefix + getElementKey(child, i);subtreeCount += mapIntoArray(child,array,escapedPrefix,nextName,callback,);}} else {// children 是一个可迭代对象,则使用 iteratorFn 函数获取迭代器,并遍历每个元素,调用 mapIntoArray 函数,并将返回的子树计数累加到 subtreeCount 中const iteratorFn = getIteratorFn(children);if (typeof iteratorFn === 'function') {const iterableChildren: Iterable<React$Node> & {entries: any,} = (children: any);const iterator = iteratorFn.call(iterableChildren);let step;let ii = 0;// $FlowFixMe[incompatible-use] `iteratorFn` might return null according to typing.while (!(step = iterator.next()).done) {child = step.value;nextName = nextNamePrefix + getElementKey(child, ii++);subtreeCount += mapIntoArray(child,array,escapedPrefix,nextName,callback,);}} else if (type === 'object') {// children 是一个对象,则检查其是否有 then 方法,如果有,则将其视为一个 Promise,并递归调用 mapIntoArray 函数if (typeof (children: any).then === 'function') {return mapIntoArray(resolveThenable((children: any)),array,escapedPrefix,nameSoFar,callback,);}// children 是一个对象,但没有 then 方法,则将其转换为字符串,并抛出一个错误,因为对象不能作为 React 子元素// eslint-disable-next-line react-internal/safe-string-coercionconst childrenString = String((children: any));throw new Error(`Objects are not valid as a React child (found: ${childrenString === '[object Object]'? 'object with keys {' +Object.keys((children: any)).join(', ') +'}': childrenString}). ` +'If you meant to render a collection of children, use an array ' +'instead.',);}}// 返回 subtreeCount,表示处理的子元素总数return subtreeCount;
}
4.4 总结

目前是第一次看这个函数的实现,知道他大概干了什么,也大概明白每一步要干什么,但是目前还不清除他为什么要这么干。为什么要来看他的实现呢?一个是只有知道他是怎么实现的,才能知道在调用这个方法时,那些情况会报错,为什么报错,能够快速处理。使用这个方法开发功能的时候能够更加得心应手。

5. React.Children.map 应用

  1. swiper 组件的开发,或者说类 swiper 组件的开发,都需要在 swiper 组件内部获取他的子元素,然后再再子元素的外层添加一个盒子,然后在盒子上实现我们需要的动画效果,比如渐入渐出、放大缩小,滑动等动画效果;
  2. 目前想到可以使用的就是瀑布流展示效果,动态获取每一列的高度,然后在动态的分配下一个子元素在那一列展示;
  3. 其他的应用应该还有很多,只是我在开发中遇到的比较少,以后有新的想法,再来补充!

http://www.ppmy.cn/server/140964.html

相关文章

红黑树及MySQL 基础架构

红黑树简介及左旋、右旋、变色 红黑树(Red Black Tree)是一种自平衡二叉搜索树(二叉查找树)&#xff0c;是一种特殊的二叉搜索树&#xff0c;在进行插入和删除时通过特定操作保持二叉树自身的平衡&#xff0c;从而获得较高的查找性能。 红黑树的平衡操作通过左旋、右旋和变色来…

万字长文解读深度学习——卷积神经网络CNN

推荐阅读&#xff1a; 卷积神经网络&#xff08;CNN&#xff09;详细介绍及其原理详解 CNN笔记&#xff1a;通俗理解卷积神经网络 文章目录 &#x1f33a;深度学习面试八股汇总&#x1f33a;主要组件输入层卷积层 (Convolutional Layer)批归一化层&#xff08;Batch Normalizat…

windows中docker安装redis和redisinsight记录

创建一个Redis运行容器&#xff0c;命令如下 docker run -it -d --name redis -p 6379:6379 redis --bind 0.0.0.0 --protected-mode no -d 代表Redis容器后台运行 --name redis 给创建好的容器起名叫redis -p 6379:6379 将容器的6379端口映射到宿主机的6379端口&#xff0c;注…

SpringBoot在线教育系统:多语言支持

5系统详细实现 5.1 普通管理员管理 管理员可以对普通管理员账号信息进行添加修改删除操作。具体界面的展示如图5.1所示。 图5.1 普通管理员管理界面 5.2 课程管理员管理 管理员可以对课程管理员进行添加修改删除操作。具体界面如图5.2所示。 图5.2 课程管理员管理界面 5.3 …

C#中lock(变量)与lock(this)的区别

在C#中&#xff0c;lock关键字用于确保线程同步&#xff0c;防止多个线程同时访问共享资源&#xff0c;从而导致数据竞争和不一致的问题。lock语句通过获取指定对象的监视器锁来实现这一功能。在使用lock时&#xff0c;可以锁定任何对象&#xff0c;包括专门创建的锁对象或现有…

异星工厂_1

经验 首次体验异星工厂这款游戏&#xff0c;得出了以下经验。 1. 基地的构建顺序&#xff1a;煤&#xff0c;电&#xff0c;原料&#xff0c;传送流&#xff0c;组装器&#xff0c;防御武器&#xff0c;其他 2. 永远不要让采集&#xff08;生产者&#xff09;停止&#xff0…

【计网不挂科】计算机网络期末考试(综合)——【选择题&填空题&判断题&简述题】完整题库

前言 大家好吖&#xff0c;欢迎来到 YY 滴计算机网络 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 本博客主要内容&#xff0c;收纳了一部门基本的计算机网络题目&#xff0c;供yy应对期中考试复习。大家可以参考 欢迎订阅 YY滴其他专栏&#xff01;…

Spark 的Standalone集群环境安装与测试

Spark 的介绍与搭建&#xff1a;从理论到实践-CSDN博客 目录 一、Standalone 集群环境安装 &#xff08;一&#xff09;理解 Standalone 集群架构 &#xff08;二&#xff09;Standalone 集群部署 二、打开监控界面 &#xff08;一&#xff09;master监控界面 &#xff…