React 前端导航

React.Children 的用法及使用场景

React.Children 的用法及使用场景

本文主要介绍 React.Children 用法及使用场景,只因为偶然间看到了React.Children.only的使用,但不知道他是干啥的,仅此记录一下。

先来看简单的个例子:

import React, { PropsWithChildren } from 'react';
const Parent = () => {
  return <Child>
    <p>这是内容111</p>
    <p>这是内容222</p>
  </Child>
}
const Child = (props: PropsWithChildren) => {
  return <>
    <h6>一共有{props.children.length}个子元素</h6>
    {props.children}
  </>
}

props.children 是我们在编写时常用的一个属性,children 可以是任何类型的值,包括:文本,JSX,DOM元素,函数等等。

我们可以从 ts 的定义看到 children 的类型:

 type PropsWithChildren<P = unknown> = P & { children?: ReactNode | undefined };
 type ReactNode = ReactElement | string | number | ReactFragment | ReactPortal | boolean | null | undefined;

props.children的类型判断

通常情况下,我们通过children传入到子组件的数据都是单一类型的,我们也明确知道是什么类型,比如上面的例子中,传入的就是DOM元素数组,我们展示了数组的个数。

但是如果我们传入的是字符传又是该怎样展示呢?

const Parent = () => {
  return <Child>
    React.Children 的用武之地
  </Child>
}

此时我们期望展示“一共有1个子元素”,但是展示出来的真实效果是“一共有20个子元素”,props.children.length 读取到的是字符串的长度,这显然是不符合期望的。

通常我们需要判断children的类型,然后再根据不同类型去写逻辑代码,这显然就增加了我们的工作量,此时 React.Children 就可以发挥作用了。

我们想正确展示children包含的元素个数,我们可以这样写 Child 组件:

const Child = (props: PropsWithChildren) => {
  return <>
    <h6>一共有{React.Children.count(props.children)}个字元素</h6>
    {props.children}
  </>
}

这只是其中一个例子,React.Children还提供了很多其他工具函数给开发者使用。

React.Children 的使用

我们从 ts 类型定义中可以看到 React.Chilren 包含以下方法:

  const Children: {
        map<T, C>(children: C | ReadonlyArray<C>, fn: (child: C, index: number) => T):
            C extends null | undefined ? C : Array<Exclude<T, boolean | null | undefined>>;
        forEach<C>(children: C | ReadonlyArray<C>, fn: (child: C, index: number) => void): void;
        count(children: any): number;
        only<C>(children: C): C extends any[] ? never : C;
        toArray(children: ReactNode | ReactNode[]): Array<Exclude<ReactNode, boolean | null | undefined>>;
    };
1. React.Children.count

React.Children.count 可以统计子组件个数。
由于 props.children 类型的不确定,我们要判断有多少个子组件就比较困难了。如果幼稚的使用 props.children.length 就很容易报错了,比如传来一个子组件"Hello World!",.length会返回12!

2. React.Children.map

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

3.React.Children.only

验证 ​children​ 是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。

4.React.Children.toArray

当你需要把子组件转化成数组时,可以使用toArray这个函数

5.React.Children.forEach

遍历子组件,与 ​React.Children.map 类似,但它不会返回一个数组。

应用场景

1.组件限制子元素类型以及数量
// 限制子元素类型
const limitChildType = (props: PropsWithChildren) =>  {
  return React.Children.map(this.props.children, child => {
    if (child.type.name !=='Col' || child.type.dispalyName !== 'Col') {
      throw new Error('the children of component Row muse be component Col')
    }
  }) 
} 

// 限制子元素数量
const limitChildCount = (props: PropsWithChildren) =>  {
  return React.Children.only(props.children);
} 
2.修改 children 的属性
const renderCustomElement = () => {
  return React.children.map(child => {
     // do something, for example, add event to props or style etc.
     return React.cloneElement(child, config)
  })
}
3.组件提供自定义触发事件元素功能

举个例子,比如 ​Form​ 组件,支持触发提交的 ​Button​ 自定义,思路是遍历 ​Form​ 的 ​children​,找到类型为 Button 的子节点,为此子节点添加 onClick 事件。

const renderSubmitButton: (submitButton: any) => any = submitButton => {
  return Children.map(child => {
    if (['boolean', 'undefined', 'string', 'number'].includes(typeof child) || child === null) {
      return child
    }
    if (child.type.name === 'Button') {
      return React.cloneElement(child, {
          onClick: () => {
            if (child.props.onClick === undefined
              || typeof child.props.onClick === 'function'
              && child.props.onClick() !== false
            ) {
              triggerSubmit()
            }
          }
        }) 
    }
    if (child.props.children) {
      return React.cloneElement(
        child,
        {},
        renderSubmitButton(child.props.children)
      )
    }
    return child
  })
}

小结

熟悉了 ​React.Children​ 的用法,相信会更有利于我们的组件开发,简化我们的代码逻辑。

声明:本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。邮箱:farmerlzj@163.com。 本站原创内容未经允许不得转载,或转载时需注明出处: 内容转载自: React前端网:https://qianduan.shop/blogs/detail/82

#react#children

相关推荐

react中实现markdown文件读取展示

react中实现markdown文件读取展示

umi实践问题汇总--持续更新

在使用umi的过程中所遇到问题的记录汇总