React props.children

发布时间 2023-04-13 11:09:37作者: 風栖祈鸢

React props.children

React 的特性,或者说 JS 的特性都太抽象勒。

无法理解

在看 React 文档的时候,以我当时的水平,看到了这个令人费解的东西:

function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="正在播放!">
        播放电影
      </AlertButton>
      <AlertButton message="正在上传!">
        上传图片
      </AlertButton>
    </div>
  );
}

按我当时的理解,Button 组件中将接收的 props 中的 children 属性作为按钮显示的文本,但这里的 AlertButton 组件没有传递 children 属性,因此按钮应该不显示内容才对。

但实际情况是,按钮不仅正常显示了:

img

而且当修改 AlertButton 组件的内容,即“播放电影”和“上传图片”时,页面按钮的文本也会改变:

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="正在播放!">
        播放电影:すずめの戸締まり
      </AlertButton>
      <AlertButton message="正在上传!">
        上传图片
      </AlertButton>
    </div>
  );
}

页面内容正常变化了:

img

这就给我整晕了,说明 AlertButton 组件的文本被当成 {children} 了,无法理解。

逐渐理解

拷问了 GPT 和百度了一下后,发现原因了:

props.children 是一个特殊的参数,它表示组件所有的子节点。 它是一个特殊的参数,是隐式传递的。在组件内部通过 props.children 就可以获得组件的子节点,有以下几种情况:

  1. 组件没有子节点,props.children 类型为 undefined;
  2. 组件有一个子节点,props.children 类型为 object;
  3. 组件有多个子节点,props.children 类型为 array。

但是使用 props.children 参数,要求组件标签必须有开始和结束标签,以标识子节点,之前都没有注意到:

function Toolbar() {
    return (
      <div>
        // 有开始和结束标签
        <AlertButton message="正在播放!">
          播放电影:すずめの戸締まり
        </AlertButton>
        <AlertButton message="正在上传!">
          上传图片
        </AlertButton>
      </div>
    );
  }

在之前(一般情况)都是直接使用自闭合标签传递 props 的,如:

function Toolbar() {
    return (
      <div>
        // 自闭合,列出 props
	<AlertButton message="正在播放!" children="播放电影:すずめの戸締まり" />
        <AlertButton message="正在上传!">
          上传图片
        </AlertButton>
      </div>
    );
  }

代码改成这样和之前效果是一样的,但这里就出现了个新问题,直接在 props 中传递 children,但又有子节点,则何如?

function Toolbar() {
    return (
      <div>
        <AlertButton message="正在播放!" children="播放电影:すずめの戸締まり" />
        // 既有子节点,又传递了 children props
        <AlertButton message="正在上传!" children="上传图片:すずめの戸締まり" >
          上传图片
        </AlertButton>
      </div>
    );
  }

如此执行了一下,第二个按钮显示的文本还是“上传图片”,看来有子节点的话,children 还是会取子节点;当把子节点删掉时,children 取的就是 props 里传递的值了。

理解一切

除了传递参数以外,children 还有一些其他用法和辅助方法。

插槽

children 可以当成 Vue 里的插槽使用,如

  function Child(props) {
    return (
      <div>
        <div>{props.children[2]}</div>
        <div>{props.children[1]}</div>
        <div>{props.children[0]}</div>
      </div>
    )
  }

  function Parent() {
    return (
      <Child>
        <div>这是0,应该排第一</div>
        <div>这是1,应该排第二</div>
        <div>这是2,应该排第三</div>
      </Child>
    )
  }

Parent 控件中传入子节点的顺序是 0、1、2,但 Child 控件中使用 children 获取到的是一个数组,所以可以按任意顺序读取,实际效果:

img

这就相当于作为插槽使用了。

React.Children

React 还提供了一些关于 children 的方法,这里简单列一下把:

  1. React.Children.map

    object React.Children.map(object children, function fn)
    

    此方法可以遍历 children 里的所有子节点,并在每个子节点中调用 fn 函数,返回一个新的 children 对象;

  2. React.Children.forEach

    React.Children.forEach(object children, function fn)
    

    也是遍历 children 里的所有子节点,并在每个子节点中调用 fn 函数,但不返回对象;

  3. React.Children.count

    number React.Children.count(object children)
    

    统计 children 中的子节点个数,返回个数;

  4. React.Children.Only

    object React.Children.only(object children)
    

    检查 children 中是否只有一个子节点,返回这个节点,如果有多个子节点则抛出异常。