一、children可以是任何类型的值
1.1 正常dom元素
<Grid>
<Row />
<Row />
<Row />
</Grid>
class Grid extends React.Component {
render() {
return <div>{this.props.children}</div>
}
}复制代码
1.2 函数
<Executioner>
{(arg) => <h1>Hello World!</h1>}
</Executioner>
class Executioner extends React.Component {
render() {
// See how we're calling the child as a function?
// ↓
return this.props.children(arg) //因为children是函数,所以这里肯定也可以传参
}
}复制代码
不只如此,children还可以是jsx等等
二、操作children
2.1 React.Children.map
<Executioner>
{(arg) => <h1>Hello World!</h1>}
</Executioner>
class Executioner extends React.Component {
render() {
// See how we're calling the child as a function?
// ↓
return this.props.children(arg) //因为children是函数,所以这里肯定也可以传参
}
}复制代码
像上图我们可以用this.props.children操作,比如我们当我们传了多个子组件之后又要怎么渲染出来呢
如果你看过React官方文档,你会看到说"子组件是一种不透明的数据结构"(opaque data structure)。意思就是props.children可以是任何类型,比如array, function, object等等。因为什么都可以传,所以你也不能确定传过来了什么东西。
<IgnoreFirstChild>
<h1>First</h1>
<h1>Second</h1> // <- Only this is rendered
</IgnoreFirstChild>
class IgnoreFirstChild extends React.Component {
render() {
const children = this.props.children
return (
<div>
{this.props.children.map((child, i) => {
// Ignore the first child
if (i < 1) return
return child
})}
// 这里我们用了this.props.children.map,并且忽略到第一个元素,但是如果这里传进来的是上面
例子是上图中的函数,这里就会报错,因为map,使用React.Children.map则不会报错
{React.Children.map(children, (child, i) => { // Ignore the first child
if (i < 1) return
return child
})}
// 这里我们用了React.Children.map,这是React提供的一些帮助函数
</div>
)
}
}复制代码
所以使用React.Children.map就没有问题了,这是一种更加稳妥的方式
<IgnoreFirstChild>
{() => <h1>First</h1>} // <- Ignored 💪
</IgnoreFirstChild>
//使用this.props.children会报错,用React.Children.map不会报错
复制代码
2.2 React.Children.count
同样由于this.props.children类型的不确定,我们要判断有多少个子组件就比较困难了。如果幼稚的使用this.props.children.length就很容易报错了。而且,如果传来一个子组件"Hello World!",.length会返回12!
class ChildrenCounter extends React.Component {
render() {
return <p>React.Children.count(this.props.children)</p>
}
}复制代码
// Renders "1"
<ChildrenCounter>
Second!
</ChildrenCounter>
// Renders "2"
<ChildrenCounter>
<p>First</p>
<ChildComponent />
</ChildrenCounter>
// Renders "3"
<ChildrenCounter>
{() => <h1>First!</h1>}
Second!
<p>Third!</p>
</ChildrenCounter>
// 每次都精准算出了子组件的个数复制代码
2.3 React.Children.toArray
当你需要把子组件转化成数组时,可以使用toArray这个函数
class Sort extends React.Component {
render() {
const children = React.Children.toArray(this.props.children)
// Sort and render the children
return <p>{children.sort().join('')}</p>
}
}
<Sort>
// We use expression containers to make sure our strings
// are passed as three children, not as one string
{'bananas'}{'oranges'}{'apples'}
</Sort>
//输出 apples bananas oranges
<Sort>
// We use expression containers to make sure our strings
// are passed as three children, not as one string
我是内容
</Sort>
//这样子也并不会报错复制代码
2.4 React.Children.only
如果你希望传进来的组件只是一个,而且是个函数
我们先借用proppropTypes来实现
class Executioner extends React.Component {
render() {
return this.props.children()
}
}
Executioner.propTypes = {
children: React.PropTypes.func.isRequired,
}
//这样就会在console里打印出日志来,但是有的时候开发者很容易忽略这些消息。这个时候我们就应该在render方
法里加入React.Children.only。
复制代码
class Executioner extends React.Component {
render() {
return React.Children.only(this.props.children)()
}
}
//如果子组件多于一个会抛出一个错误,整个app会停止--绝对不会让一些偷懒的开发搞乱我们的组件。
复制代码
2.5 React.cloneElement
比如我们有一个RadioGroup组件,该组件包含一些RadioButton子组件(最终渲染成<input type="radio"> )。
RadioButtons不是由RadioGroup渲染的,它们作为子组件传入,这意味着我们的代码看起来像这样:
render() {
return(
<RadioGroup>
<RadioButton value="first">First</RadioButton>
<RadioButton value="second">Second</RadioButton>
<RadioButton value="third">Third</RadioButton>
</RadioGroup>
)
}复制代码
但是这样子的话RadioButton不会出现在一个RadioGroup中,所以我们必须给所有的RadioButton加上同一个属性name
<RadioGroup>
<RadioButton name="g1" value="first">First</RadioButton>
<RadioButton name="g1" value="second">Second</RadioButton>
<RadioButton name="g1" value="third">Third</RadioButton>
</RadioGroup>
//这种方法显然可以,但是有点笨复制代码
我们可以先用React.children改写一下
class RadioGroup extends React.Component {
constructor() {
super()
// Bind the method to the component context
this.renderChildren = this.renderChildren.bind(this)
}
renderChildren() {
return React.Children.map(this.props.children, child => {
// TODO: Change the name prop to this.props.name
return child
})
}
render() {
return (
<div className="group">
{this.renderChildren()}
</div>
)
}
}
//这里问题就是我们还是没有给每个RadioButton加上property name
复制代码
这里我们用上我们最后的一个api React.cloneElement
const cloned = React.cloneElement(element, {
new: 'yes!'
})
第一个参数是拷贝源的React element, 第二个参数是prop object,clone以后会把这个prop object设置成属
性给拷贝结果。cloned元素将持有名为new的属性,属性值为"yes!"复制代码
那么这里我们就来改写上面的代码
class RadioGroup extends React.Component {
constructor() {
super()
// Bind the method to the component context
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="g1">
<RadioButton value="first">First</RadioButton>
<RadioButton value="second">Second</RadioButton>
<RadioButton value="third">Third</RadioButton>
</RadioGroup>
//这样子只需要写个一个name便可以实现功能