React高阶组件HOC

1,043 阅读4分钟

@[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而非基类

五、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增加必要的方法,如图

在这里插入图片描述