1. 概念
很多情况下,需要给多个组件添加或者修改一些特定的props,或者在所有组件基础上加个水印等等。而如果这个功能如果是针对多个组件的,每一个组件都写一套相同的代码,显得不是很明智,所以就可以考虑使用高阶组件。
ReactNative的高阶组件也叫HOC(全称Higher-order component),构建方式主要有属性代理
和反向继承
。主要用于:
- 组件代码复用,代码模块化
- 增删改props
- 渲染劫持
- ……
所以高阶组件经常作为一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
2. 使用
2.1 组件代码复用,代码模块化
下面是一个简单的高阶组件,给传进去的组件前面加了一个固定的Text组件。
# 定义
export const HocComponent = (param) => (WrappedComponent) => {
class NewComponent extends React.Component {
render() {
return(
<View>
<Text>我是高阶组件,传进来参数是:{JSON.stringify(param)}</Text>
<WrappedComponent {...this.props}/>
</View>
)
}
}
return NewComponent
}
HocComponent 是一个函数,param是需要传入组件的参数,WrappedComponent是传入的组件,NewComponent就是传入参数后生成的新的组件。
使用方式推荐用装饰器语法, 如下图就可以给TestComp和TestComp2组件传入参数都加上一个Text组件。
# 装饰器调用
@HocComponent({name:'zhangsan'})
export class TestComp extends React.Component<IProps> {
...
}
# 装饰器调用
@HocComponent({name:'lisi'})
export class TestComp2 extends React.Component<IProps> {
...
}
# 函数调用 ( HocComponent改成返回<NewComponent/>)
const TestComp = HocComponent({name:'zhangsan'})(TestComp )
2.2 增删改Props
也可以在基础上加入特定props,例如在高阶组件内部自定义一个颜色主题themeType,这样就可以在新返回的组件通过this.props.themeType获取当前的颜色主题。
export default (Comp)=>{
class newCom extends React.Component{
const newProps = {
...this.props,
themeType:'dark'
}
render() {
return <Comp {...newProps}/>
}
}
return newCom
}
2.3 渲染劫持
在render方法里控制显示渲染逻辑,下面是一个例子。
当属性this.props.loading为true时显示加载组件,当属性this.props.data数据为空时显示空白组件,正常则直接显示渲染传入的<Comp/>组件。
const HocComponent = (WrappedComponent)=>{
newComp extends WrappedComponent {
render(){
if(this.props.loading){
return <View><Text>加载中</Text></View>
}
if(this.props.data.length>0){
return <View><Text>暂无数据</Text></View>
}
return super.render();
}
}
return newComp
}
需要注意的是:
- 普通组件的 static方法怎么传入高阶组件内部?
当使用高阶组件包装组件,原始组件被容器组件包裹,也就意味着新组件会丢失原始组件的所有静态方法。解决这个问题的方法就是,将原始组件的所有静态方法全部拷贝给新组件:
# 普通组件内部定义了 static 方法
static ABC(){
return 'abc'
}
# 高阶组件内部
NewComponent.ABC = WrappedComponent.ABC
3. 属性代理与反向继承
对于函数内部高阶组件的生成主要由以下两种:
- 属性代理:高阶组件通过
包裹
传进来React组件进行操作; - 反向继承:高阶组件
继承
于被包裹的React组件进行操作。
上面所说的(1)(2)主要是属性代理的使用方式,(3)反向继承的案例,下面是反向继承的详细案例。
const HocComponent = (WrappedComponent)=>{
newComp extends WrappedComponent {
// 此处重写了父类的方法父类就不会再执行 componentDidMount()
componentDidMount() {
console.log('1')
// 修改父类的 state
this.setState({
result: '通过高阶组件(反向继承方式)创建的组件'
})
}
render(){
return super.render();
}
}
return newComp
}
属性代理和反向继承主要的区别是
- 属性代理:灵活操作组件的props,如上述的增删改props,再把props传给组件。
- 反向继承:拦截生命周期、state、渲染过程。因为继承了传进来的组件,如果新组件写了componentDidMount等方法,会覆盖掉原方法;也可以在外部组件调用被继承组件的方法,如super.render();
4. 总结
这篇文章主要讲解了HOC的概念和使用思路,需要注意的是,在创建HOC的过程中尽量不要改变原始组件,而是使用组合的方式。HOC的实际使用场景要比现在讲的还要多,例如页面权限管理、数据组装关联、监控日志打印、埋点上报等等,灵活运用好HOC能够对RN的架构逻辑起到很好的帮助与扩展。
参考
React高阶组件中文文档
React Native高阶组件(HOC)模型理论与实践
React-Native 高阶组件