@[toc]
React高阶组件HOC
一、定义 (什么是高阶组件)
首先高阶组件是一种“状态逻辑复用”框架技术,React中常用的主要有mixin、hoc、hooks。
何为高阶组件:通过函数向现有组件类添加逻辑,一个React组件包裹另外一个React组件,就是高阶组件HOC(Higher Order Component)。
高阶组件是特别的高阶函数(类的本质就是一个函数,组件就是一个函数)。高阶组件接收一个组件函数, 返回是一个新的组件函数。高阶函数概念来源于JavaScript的高阶函数:高阶函数就是接受函数作为输入或者输出的函数
二、使用场景 (高阶组件有什么用)
1、代码复用,代码模块化
如果这个功能是针对多个组件的,同时每一个组件都写一套相同的代码,明显显得不是很明智,所以就可以考虑使用HOC。
- 举例。这里对每个页面新增props弹窗功能
function withPopupComponent(C) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
popup: null,
modal: null,
modalVisible: false,
bgStyle: {},
onRequestClose: null,
};
}
componentDidMount() {
}
componentWillUnmount() {
this.setState({
pop: null,
modal: null,
modalVisible: false
})
}
modal = (modal, bgStyle = {}, isBgTouch, onRequestClose) => {
this.setState({
modal: modal,
modalVisible: React.isValidElement(modal),
bgStyle,
isBgTouch: !!isBgTouch,
onRequestClose: onRequestClose,
})
};
popup = (popup = null, popContainerStyle = {}) => {
if (_.contains(Const.androidFinishPage, global.curRouteName)) {
eventEmitterUtils.activateCallBack('HiddenTabBarKey', { hidden: React.isValidElement(popup) });
}
this.setState({
popup: popup,
popContainerStyle,
})
};
render() {
const { modalVisible, modal, bgStyle, isBgTouch, onRequestClose, popup, popContainerStyle } = this.state;
return (
<View style={{ flex: 1 }}>
<C modal={this.modal} popup={this.popup} {...this.props} />
<ModalView modalVisible={modalVisible}
bgStyle={bgStyle}
isBgTouch={isBgTouch}
onRequestClose={() => {
if (onRequestClose) {
onRequestClose()
} else {
this.setState({
modalVisible: false,
modal: null,
onRequestClose: null,
})
}
}}>
{modal}
</ModalView>
{
React.isValidElement(popup) ? <Popup popup={popup} style={popContainerStyle} /> : null
}
</View>
)
}
}
}
2、增删改props
你想要给wrappedComponent增加一个props,所以就可以考虑使用HOC。
- 举例。对wrappedComponent增加一个props
function control(wrappedComponent) {
return class Control extends React.Component {
render(){
let props = {
...this.props,
message: "You are under control"
};
return <wrappedComponent {...props} />
}
}
}
3、渲染劫持
这里的渲染劫持并不是你能控制它渲染的细节,而是控制是否去渲染。由于细节属于组件内部的render方法控制,所以你无法控制渲染细节。
比如,组件要在data没有加载完的时候,现实loading...,不会显示组件的具体内容。或者页面权限管理等
- 举例。组件要在data没有加载完的时候,现实loading...
function loading(wrappedComponent) {
return class Loading extends React.Component {
render(){
if(!this.props.data) {
return <div>loading...</div>
}
return <wrappedComponent {...props} />
}
}
}
三、写法(两种实现方式,三种写法)
3.1、Props Proxy (PP-属性代理)
type1 PP-属性代理jsx组件式写法
/**
* type1 PP-属性代理jsx组件式写法
*/
const HOC1 = (WrappedComponent) => class WarppedComponent extends Component {
render() {
return (
<View style={styles.container}>
<WrappedComponent {...this.props} />
<TouchableOpacity style={styles.button}>
<Text style={styles.text}>type1:PPHOC对Props属性增删查改</Text>
</TouchableOpacity>
</View>
)
}
}
type2 PP-属性代理js函数式写法
/**
* type2 PP-属性代理js函数式写法
*/
const HOC2 = (WrappedComponent) => {
const targetRender = WrappedComponent.prototype.render;
return WrappedComponent.prototype.render = function () {
return (<View style={styles.container}>
{
targetRender.call(this)
}
<TouchableOpacity style={styles.button}>
<Text style={styles.text}>type2:PPHOC对Props属性增删查改</Text>
</TouchableOpacity>
</View>);
};
}
3.2、Inheritance Inversion (II-反向继承)
type3 Inheritance Inversion (II-反向继承)
/**
* type3 Inheritance Inversion (II-反向继承)
*/
const HOC = (WrappedComponent) => class extends WrappedComponent {
render() {
return (
<View style={styles.container}>
{super.render()}
<TouchableOpacity style={styles.button}>
<Text style={styles.text}>type3:IIHOC对Props属性增删查改</Text>
</TouchableOpacity>
</View>
);
}
}
四、基类还是高阶组件的选择
-
基类的优点
- 1、子类可以较为简单的复用父类的方法
-
基类的缺点
- 1、继承意味着子类无差别的拥有父类方法,这样会造成子类代码污染
- 2、特别在React中父类控制子类页面渲染逻辑,会造成子类的生命周期紊乱,无法使用最原始的生命周期方法。
五、HOC示例
5.1、react-navigation中的使用
参考"react-navigation": "3.6.1"中/node_modules/react-navigation-tabs/dist/views/BottomTabBar.js
5.2、react官网示例
这个是官网上的一个示例,可以用来监控父级组件传入的props的改变
function logProps(InputComponent) {
InputComponent.prototype.componentDidUpdate = function(prevProps) {
console.log('Current props: ', this.props);
console.log('Previous props: ', prevProps);
};
// The fact that we're returning the original input is a hint that it has
// been mutated.
return InputComponent;
}
// EnhancedComponent will log whenever props are received
const EnhancedComponent = logProps(InputComponent);
六、使用HOC需要注意什么
6.1、尽量不要随意修改下级组件需要的props
所以这么说,是因为修改父级传给下级的props是有一定风险的,可能会造成下级组件发生错误。比如,原本需要一个name的props,但是在HOC中给删掉了,那么下级组件或许就无法正常渲染,甚至报错。
6.2、Fast Refresh会丢失hoc新增的方法
这种错误是hot热加载Fast Refresh在页面动态更新的时候只加载了页面,没有加载hoc导致的。处理方法是 1、使用hot reload不使用Fast Refresh即可 2、或者修改/node_modules/react/cjs/react.development.js增加必要的方法,如图