介绍俩日常没使用的React API,近日踩雷了,遂借此篇提出来品品...javascript
首先这俩货同属于React的顶层API,即咱们import React from 'react';
后,能够经过React.xxx
的方式来调用。html
再看官方文档对它们的划分:java
图中的几个API都是对React
元素进行操做的,isValidElement
就不赘述了,用来校验入参是不是一个合法的React
元素,返回一个布尔值。node
咱们都知道在props
对象中还有children
这个属性。它可以从某种程度上减小咱们在一个组件内的嵌套层级,可能这样描述有点抽象,举个栗子:react
// 好比咱们有个Modal模态框组件
export default class Modal extends React.Component {
//...
}
// 有不少场景须要在Modal框内展现子组件的东西,最多见的结构相似下面
<Modal>
<Content /> </Modal>
// 的确能够在定义Modal的文件内import子组件,但咱们这是一个公共的组件,它仅是一个套套,因此一般会使用下面这种方案
render() {
return (
<div> {this.props.children} </div>
)
}
复制代码
这样来讲,咱们的父组件就和可能传入的children
解耦了,各个模块都是独立的,各司其职。更多的关于props.children
的语法阐释能够阅读官方文档。数组
看到这里,咱们也发现了一个问题,就是props.children
对于咱们开发者来讲就是一个黑盒,咱们对它可能传入的数据结构是不可知的(表达式、布尔、render function
等等),若是咱们没有对其进行操做,那其实没什么所谓。但只要咱们对其进行操做了,好比下意识觉得是个数组进行props.children.map
这样的调用就要注意,非Array
就直接报TypeError
了。那怎么处理相似这样的情景呢?数据结构
其实React.Children
刚好就是为咱们提供处理props.children
数据结构能力的API。注意这里React.Children
的Children
是大写。dom
React.Children.map(children, function[(thisArg)])
这个类方法可以cover前文我提到的未知数据结构下的遍历问题,只须要简单修改:函数
React.Children.map(props.children, child => {})
复制代码
能够看到这个API接收两个参数,第一个就是咱们一般要处理的黑盒prop.children
,第二个入参回调,其实就是咱们遍历的元素上下文,经过它,咱们可以进行定制化的操做。this
笔者结合源码获得当props.children
为null
和undefined
时,最终会原值返回,其他情景则是返回一个数组。
跟React.Children.map
相似,都是迭代操做,只不过这个不会返回数组。undefined
和null
时的判断逻辑同上。
返回其中内部元素数,其值与前面两个迭代方法的回调触发次数相等。
用于判断传入的children
是否只有一个child
。注意接收类型是React element
。不能拿React.Children.map()
返回的结果再去判断是几个child
,由于此时你拿到的已然是一个Array
类型。
这个API会将黑盒的props.children
数据结构以扁平的Array
结构暴露给咱们,以下面这样:
经常使用在往下传props
时,从新排序或过滤部分children
的情景。
有了上面的铺垫,这个API的引入就比较天然了,前文中咱们经过React.Children
的类方法获得了访问本是黑盒的props.children
的能力。React.cloneElement
则是能让咱们在操做React element
时,进行浅层的新props merge
,传入的新children
则会替换旧的children
。原element
的key
和ref
都会保留。
看下API定义:
React.cloneElement(
element,
[props],
[...children]
)
复制代码
其实跟React.createElement
的构造有点像:
React.createElement(
type,
[props],
[...children]
)
复制代码
毕竟是拷贝返回一个新的组合元素,React.cloneElement
处理element
时能够大体理解成<element.type {...element.props} {...props}>{children}</element.type>
。
那这个API到底有啥用呢?举一个场景:
<Tabs active=''>
<Tab id='a' title='a'>
Content: {Math.random()}
</Tab>
<Tab id='b' title='b'>
Content: {Math.random()}
</Tab>
<Tab id='c' title='c'>
Content: {Math.random()}
</Tab>
</Tabs>
复制代码
我但愿点击对应Tab
的时候,再显示Content信息,而且再也不修改以上组件结构(不额外在每一个子组件上加onClick
的props
),实际展现相似下图:
此时,咱们已经了解了前文中介绍的API的能力,大体有两种解决方案,主体思路是一致的,区分在是否是每一个子组件都挂一个回调亦或在父组件上挂一个事件代理,去判断。
这里我使用HOOKS的函数式写法:
// 事件代理
const Tabs = props => {
const { children, ...rest } = props;
const [active, setActive] = useState(rest.active);
let handleClick = e => {
if (e.target.nodeName === 'A') {
setActive(e.target.id);
}
}
return (
<header> <nav className={styles.nav}> <ul onClick={handleClick}> { React.Children.map(children, child => React.cloneElement(child, {active: active})) } </ul> </nav> </header>
)
}
复制代码
// 每个child 都绑定回调 经过cloneElement传props
const Tabs = props => {
const { children, ...rest } = props;
const [active, setActive] = useState(rest.active);
let toggleActive = (e, id) => {
e.preventDefault();
setActive(id);
}
return (
<header> <nav className={styles.nav}> <ul> { React.Children.map(children, child => React.cloneElement(child, {active: active, toggleActive: toggleActive})) } </ul> </nav> </header>
)
}
复制代码
主体思想都相似,就是把子组件须要的属性和回调函数经过cloneElement
的方式merge
进去。
以上DEMO,可借此传送门移步。
React.Children
提供了咱们直接访问黑盒props.children
数据结构的能力;
React.cloneElement
接收一个React element
并支持往其中浅层合并props
,替换旧children
;笔者看来该API能够从必定程度上减小代码的重复书写,使组件标签表达更加清晰。