react
本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该方法和他的实现原理。
cloneElement
方法接收三个参数,第一个参数是一个 react
元素,可以是真实的 dom 结构也可以是自定义的组件;第二个参数返回旧元素的 props
。可以添加新的 props
进行拓展;第三个是 props.children
,不指定默认展示我们调用时添加的子元素。如果指定会覆盖我们调用克隆组件时里面包含的元素。
注意:
react
元素就是一个普通的对象,包含$$typeof
、ref
、children
等属性,我们第一小节书写过
使用
我们这里可以配合上一小节提到的高阶组件实现 -- 反向继承。
代码语言:txt复制// src/index.js
// 定义一个 button 组件
class Button extends React.Component {
state = {name: 'xxx'}
componentDidMount() {
console.log('button componentDidMount')
}
render() {
console.log('button render')
return <button>
<span>button:</span> // 会被覆盖
</button>
}
}
// 定义高阶组件函数
const wrapper = OldComponent => {
// 这里不继承 React.Component 了,继承传入的组件
return class extends OldComponent {
constructor(props) {
super(props)
this.state = {
number: 0
}
}
componentDidMount() {
console.log('wrapper componentDidMount')
super.componentDidMount()
}
handleClick = () => {
this.setState({
number: this.state.number 1
})
}
render() {
console.log('wrapper render')
// 使用 super.xx 调用父组件方法
let renderElement = super.render() // 拿到父组件渲染 vdom
let newProps = {
...renderElement.props,
...this.state,
onClick: this.handleClick
}
// 参数:要渲染的元素,元素的属性,元素的子元素
return React.cloneElement(renderElement, newProps, this.state.number)
}
}
}
这里我们使用的 react
原生库,可见子元素直接进行了覆盖。同时先执行的父组件,在执行的子。基于反向继承,我们可以实现拦截生命周期、state
、渲染过程,有种 AOP
编程的感觉。
实现
代码语言:txt复制// src/react.js
function cloneElement(element, newProps, ...newChildren) {
// 这里对子元素进行了过滤,大家还记得 toVdom 么,把字符串和数字处理成对象形式
let children = [...newChildren].filter(el => el !== undefined).map(toVdom)
// 对react子元素判断区分个数,长度大于 1 的使用数组进行 diff,一个元素的话直接判断类型
if (children.length === 1) {
children = children[0]
}
let props = {
...element.props,
...newProps,
children // 直接用children把自带的覆盖
}
return {
...element,
props
}
}
切换为自己的库,可以看到效果和原生库一样。本节内容同样不是很多,但对于理解 cloneElement
实现机制足够了,感兴趣的小伙伴可以尝试在工作中使用或者网上查看更多的使用示例。如有错误欢迎指正!