前言:本文介绍了顶层API的使用,大多数情况,我们都是对市面上的封装的组件拿来即用,所以几乎用不到任何顶层API来写通用型的业务代码。但是往往都是这些顶层API的支持得以我们在用这些组件时候得心应手。所以,了解还是需要的,我这里大致拿它用法来做浅析。
1、React.Children.map
每个
this.props.children调用后面的function方法,并且这个api最终返回一个数组 语法:
React.Children.map(children, function[(thisArg)])
它容易让人与js的数组map方法产生误解,实际上它与数组的map完全不是一个东西。什么意思呢?我们写段代码就知道了,假设我们代码是这样(这种组件写法方式在antd中非常多:steps、tabs等等):
// 子组件
function Child(props) {
return (
<div>
do something...
</div>
);
}
// 父组件
function Father(props) {
return (
<div>
{props.children}
</div>
);
}
// 使用
function Test() {
return <div>
<Father>
<Child>111</Child>
<Child>2222</Child>
</Father>
</div>;
}
如果现在我们需要用React.Children.map来给每个子组件加个什么属性的话(需要和React.cloneElement配合,待会讲)就如下:
import React, { Component } from 'react';
// 子组件
function Child(props) {
return (
<div>
do something...
</div>
);
}
// 父组件
function Father(props) {
//看这里,它是把children传给map,后面的函数会依次执行,第一参数就是它本身
const reactNode = React.Children.map(props.children, function(item, index) {
console.log('item', item);
return item;
});
return (
<div>
{reactNode}
</div>
);
}
// 使用
function Test() {
return <div>
<Father>
<Child>111</Child>
<Child>2222</Child>
</Father>
</div>;
}
上面的item打印出来结果:打印了了两个item,因为有两个子节点。
并且reactNode的值是为数组
[item,item]
同等写法,可以用props.children.map
不同点是这个map用的数组的map,相当于我们直接拿子节点来遍历了。
const reactNode = props.children.map((item, index) => {
return item;
});
而antd中使用的是toArray
import toArray from 'rc-util/lib/Children/toArray';
const reactNode = toArray(props.children).map((child, index) => {
return child
})
实际上效果都是一样的。
2、React.Children.forEach
它的用法和React.Children.map相同,唯一区别是它没有返回值,相对用的少。
3、React.cloneElement(重点)
以 element 元素为样板克隆并返回新的 React 元素 语法:
React.cloneElement(
element,
[config],
[...children]
)
它的使用效果同于:
const nreProps = {...props, test:1}
<div {...newProps}>{children}</div>
把props拷贝并且赋予新的props,如果熟悉高阶组件HOC的同学一定知道能一下明白。
我们开头列举的React.Children.map来结合它使用一下看看效果:
import React, { Component } from 'react';
// 子组件
function Child(props) {
return (
<div>
{props.children}
</div>
);
}
// 父组件
function Father(props) {
const reactNode = props.children.map((child, index) => {
return React.cloneElement(child, {
...child,
style: { color: props.color },
});
});
return (
<div>
{reactNode}
</div>
);
}
// 使用
function Test() {
return <div>
<Father color='red'>
<Child>111</Child>
<Child>2222</Child>
</Father>
</div>;
}
在保持子组件不变的情况下 父组件很轻松的把样式红色传递给了子组件。它在antd的中作为组件设计使用可以说是一个很频繁的顶层API,以Tabs为例子我们来对比Tabs组件的使用:
差的组件设计❌:
<Tabs>
<TabPan active={true} onclick={this.onclick} key='1'> 111 </TabPan>
<TabPan active={false} onclick={this.onclick} key='2'> 222</TabPan>
<TabPan active={false} onclick={this.onclick} key='3'> 333</TabPan>
</Tabs>
激活的key和点击事件写在每个子组件上,变得非常繁琐。如果用cloneElement来设计的话,就会非常好用。
好的组件设计(antd中)✔:
<Tabs onclick={this.onclick} activeKey='1'>
<TabPan key='1'> 1111 </TabPan>
</Tabs>
对比结果一目了然。我们可以用不到50行的代码来实现同等antd的组件调用方式:
import React, {Component} from 'react';
class Tabs extends Component {
state = {
activeKey: this.props.defaultActiveKey
}
onChange = (activeKey) => {
this.setState({
activeKey
})
this.props.onChange(activeKey)
}
render() {
return (
<div>
{this.props.children.map((child, index) => {
return React.cloneElement(child, { // 通过克隆可以修改子组件的属性值
...child,
active: child.props.id === this.state.activeKey,
key: child.props.id,
activeKey: this.state.activeKey,
onChange: () => this.onChange(child.props.id)
})
})}
</div>
);
}
}
class TabPane extends Component {
render() {
return (
<div>
<h1 style={{color: this.props.active && 'red'}} onClick={this.props.onChange}>
{this.props.tab}
</h1>
<div> {(this.props.activeKey === this.props.id) && this.props.children}</div>
</div>
);
}
}
使用:
import React, {Component} from 'react';
class Index extends Component {
callback = (key) => {
console.log('key', key)
}
render() {
return (
<div>
<Tabs defaultActiveKey={1} onChange={this.callback}>
<TabPane id={1} tab='tab-1'>
Content of Tab Pane 111
</TabPane>
<TabPane id={2} tab='tab-2'>
Content of Tab Pane 2222
</TabPane>
<TabPane id={3} tab='tab-3'>
Content of Tab Pane 333
</TabPane>
</Tabs>
</div>
);
}
}
最终效果:
这就是cloneElement带来的优势。