用生命谱写代码的赞歌

0%

React.Children 用法

React.Children

React.Children 为处理 this.props.children 的未知数据结构提供了解决方法。

  • 任何东西都能是一个child
1
2
3
4
5
<Grid>
<Row />
<Row />
<Row />
</Grid>

React中的 Children 不一定是组件,它们可以使任何东西。例如,我们能够直接将文字作为 children 传递我们的 <Grid /> 组件。
JSX将会自动删除每行开头和结尾的空格,以及空行。它还会把字符串中间的空白行压缩为一个空格。

1
<Grid>Hello world!</Grid>

我们能够传递任何的 JavaScript表达式 作为children,包括函数。

1
2
3
4
5
6
class Executioner extends React.Component {
render() {
// See how we're calling the child as a function?
return this.props.children()
}
}

我们可以像下面这样使用上面的组件

1
2
3
<Executioner>
{() => <h1>Hello World!</h1>}
</Executioner>

React.Children.map

1
2
3
4
5
6
7
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>
})
// 等价于
this.props.children.forEach(function (child) {
return <li>{child}</li>
})

需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined;如果有一个子节点,数据类型是 object;如果有多个子节点,数据类型就是 array。所以,处理 this.props.children 的时候要小心。

React 提供一个工具方法 React.Children 来处理 this.props.children。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是object

1
2
3
4
5
6
7
8
9
10
11
<NotesList>
<span>hello</span>
<span>hello</span>
</NotesList>
//返回两个子节点

<NotesList></NotesList>
//返回undefined

<NotesList>null</NotesList>
//返回null

React.Children.forEach

React.Children.forEach(children, function [(this.Arg)])

类似于 React.Children.map(),但是不返回对象。

React.Children.count

number React.Children.count(children)

返回 children 当中的组件总数,和传递给 map 或者 forEach 的回调函数的调用次数一致。

React.Children.only

object React.Children.only(children)

1
2
3
4
5
6
// 上面的 <Executioner /> 组件,它只能在传递单一child的情况下使用,而且child必须为函数。可以像下面这么写
class Executioner extends React.Component {
render() {
return React.Children.only(this.props.children)()
}
}

返回 children 中 仅有的子级。否则抛出异常。

这里仅有的子级,only 方法接受的参数只能是一个对象,不能是多个对象(数组)。

实际应用(编辑 children )

我们可以将任意的组件呈现为 children ,但是仍然可以用父组件去控制它们,而不是用渲染的组件。为了说明这点,让我们举例一个能够拥有很多 RadioButton 组件的 RadiaGroup 组件。

RadioButton 不会从 RadioGroup 本身上进行渲染,它们只是作为 children 使用。这意味着我们将会有这样的代码。

1
2
3
4
5
6
7
8
9
render() {
return(
<RadioGroup>
<RadioButton value="first">First</RadioButton>
<RadioButton value="second">Second</RadioButton>
<RadioButton value="third">Third</RadioButton>
</RadioGroup>
)
}

为了把 input 标签弄到同组,必须拥有相同的 name 属性。当然我们可以直接给每个RadioButton 的 name 赋值,但是这个是无聊的并且容易出错。

1
2
3
4
5
<RadioGroup>
<RadioButton name="g1" value="first">First</RadioButton>
<RadioButton name="g1" value="second">Second</RadioButton>
<RadioButton name="g1" value="third">Third</RadioButton>
</RadioGroup>

改变 children 属性

在RadioGroup 中我们将会添加一个叫做 renderChildren 的方法,在这里我们编辑children的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class RadioGroup extends React.Component {
constructor() {
super()
// Bind the method to the component context
this.renderChildren = this.renderChildren.bind(this)
}

renderChildren() {
// TODO: Change the name prop of all children
// to this.props.name
return this.props.children
}

render() {
return (
<div className="group">
{this.renderChildren()}
</div>
)
}
}

那么如何编辑它们的属性呢?

永恒地克隆元素

React.cloneElement 会克隆一个元素。我们将想要克隆的元素当作第一个参数,然后将想要设置的属性以对象的方式作为第二个参数。

1
2
3
4
5
6
7
renderChildren() {
return React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
name: this.props.name
})
})
}

最后一步就是传递一个唯一的 name 给RadioGroup

1
2
3
4
5
<RadioGroup name="g1">
<RadioButton value="first">First</RadioButton>
<RadioButton value="second">Second</RadioButton>
<RadioButton value="third">Third</RadioButton>
</RadioGroup>