# 关于props.children的一切

# 组件的props.children是指什么?

React支持在自定义组件时,在其中嵌套 JSX 结构。嵌套的结构在组件内部都可以通过 props.children 获取到,这种组件编写方式在编写容器类型的组件时非常有用:

class Card extends Component {
  render () {
    return (
      <div className='card'>
        <div className='card-content'>
          {this.props.children}
        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <Card>
    <h2>React.js</h2>
    <div>开源</div>
    订阅:<input />
  </Card>,
  document.getElementById('root')
)

props.children打印结果如下:

React.js 把嵌套的 JSX 元素一个个都放到数组当中,然后通过 props.children 传给了 Card。

# React.Children 操纵子元素

React.Children 提供了用于处理 this.props.children 不透明数据结构的辅助方法。

这里的“不透明”,代表 this.props.children 可以是任何类型,例如数据,函数,对象,等等。因此你可以传递任何东西。

props.children类似于vue中插槽,插入的child可以是任意类型。并且,插入不同的内容可能会得到不同类型的props.children

  • 当不插入内容或内容为换行符时,props.children为undefined
  • 当插入多个内容时,props.children为Array多个文本视为一个内容
  • 当插入一个内容时,props.children为插入值

因为 props.children并不一定为数组,所以在使用数组方法时记得要判断是否为数组,或者使用帮助方法 React.Children.map 和 React.Children.forEach,这样即使不是数组也不会报错。

# 1、遍历当前组件的children

React.Children.map(children, function(child, i) {}) // 遍历,并返回一个新数组;

React.Children.forEach(children, function(child, i) {}) // 只遍历

# 2、传递不同类别的children有何区别?如何获取children的数量?

  • 如果传递一个字符串或者函数作为子元素,将打破this.props.children.length的正常使用:比如有一个后代,“Hello World.”,但是this.props.children.length相反却输出12!
  • 而不管子元素是什么类型,React.Children.count(children)都可以准确地返回 children 中的组件总数量,等同于通过 map 或 forEach 调用回调函数的次数。
const ChildCount = props => Children.count(props.children);

// 下面渲染结果(即props.children的长度是什么):
<ChildCount>1</ChildCount> // 1
<ChildCount> 1 </ChildCount> // 1
<ChildCount> 1  2 </ChildCount> // 1
<ChildCount> 1  {2} </ChildCount> // 2

# 3、在渲染函数中如何操作子节点的集合(数组化)

React.Children.toArray(children),将 children 这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 key。当你想要在渲染函数中操作子节点的集合时,它会非常实用,特别是当你想要在向下传递 this.props.children 之前对内容重新排序获取子集时。

# 4、验证只有一个子React元素

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

# 5、修改子元素属性

当想要给多个子元素添加属性时,比如这里为了分组,需要给RadioButton添加相同的name属性:

render () {
  return (
    <RadioGroup>
      <RadioButton value="first"> First </RadioButton>
      <RadioButton value="second"> Second </RadioButton>
      <RadioButton value="third"> Third</RadioButton>
    </RadioGroup>
  );
}

// 我们当然可以遍历,然后给每一个单独的RadioButton分配一个name属性。
<RadioGroup>
  <RadioButon name="g1" value="first">First</RadioButton>
  <RadioButton name="g1" value="second">Second</RadioButton>
  <RadioButton name="g1" value="third">third</RadioButton> 
</RadioGroup>

我们当然可以这样直接手动给每个RadioButton添加name="g1"属性,但这并不是聪明的做法,我们可以使用React.cloneElement()这样做:

class RadioGroup extends React.Component {
    constructor() {
        super();
        this.renderChildren = this.renderChildren.bind(this);
    }
    renderChildren() {
        return React.Children.map(this.props.children, child => {
            return React.cloneElement(child, {
                name: this.props.name
            })
        })
    }
    render () {
        return (
        <div className="group">
            {this.renderChildren()}
        </div>
        );
    }
}

// 此时,只需给我们的RadioGroup组件设置一个唯一name属性值:
<RadioGroup name="g1">
  <RadioButton value="first">First</RadioButton>
  <RadioButton value="second">Second</RadioButton>
  <RadioButton value="three"> Three</RadioButton>
</RadioGroup>
Last Updated: 10/9/2020, 5:51:45 PM