React高阶组件(HOC)模子理论与实践

什么是HOC?

HOC(全称Higher-order component)是一种React的进阶运用要领,重要照样为了便于组件的复用。HOC就是一个要领,猎取一个组件,返回一个更高等的组件。

什么时刻运用HOC?

在React开辟过程当中,发现有许多情况下,组件须要被”加强”,比方说给组件增添也许修正一些特定的props,一些权限的治理,也许一些其他的优化之类的。而假如这个功用是针对多个组件的,同时每个组件都写一套雷同的代码,显著显得不是很明智,所以就能够斟酌运用HOC。

栗子:react-redux的connect要领就是一个HOC,他猎取wrappedComponent,在connect中给wrappedComponent增添须要的props。

HOC的简朴完成

HOC不仅仅是一个要领,确实说应该是一个组件工场,猎取低阶组件,天生高阶组件。

一个最简朴的HOC完成是这个模样的:

function HOCFactory(WrappedComponent) {
  return class HOC extends React.Component {
    render(){
      return <WrappedComponent {...this.props} />
    }
  }
}

HOC能够做什么?

  • 代码复用,代码模块化
  • 增编削props
  • 衬着挟制

实在,除了代码复用和模块化,HOC做的实在就是挟制,由于传入的wrappedComponent是作为一个child举行衬着的,上级传入的props都是直接传给HOC的,所以HOC组件具有很大的权限去修正props和掌握衬着。

增编削props

能够经由过程对传入的props举行修正,也许增添新的props来到达增编削props的效果。

比方你想要给wrappedComponent增添一个props,能够这么搞:

function control(wrappedComponent) {
  return class Control extends React.Component {
    render(){
      let props = {
        ...this.props,
        message: "You are under control"
      };
      return <wrappedComponent {...props} />
    }
  }
}

如许,你就能够在你的组件中运用message这个props:

class MyComponent extends React.Component {
  render(){
    return <div>{this.props.message}</div>
  }
}

export default control(MyComponent);

衬着挟制

这里的衬着挟制并非你能掌握它衬着的细节,而是掌握是不是去衬着。由于细节属于组件内部的render要领掌握,所以你没法掌握衬着细节。

比方,组件要在data没有加载完的时刻,实际loading…,就能够这么写:

function loading(wrappedComponent) {
  return class Loading extends React.Component {
    render(){
      if(!this.props.data) {
        return <div>loading...</div>
      }
      return <wrappedComponent {...props} />
    }
  }
}

这个模样,在父级没有传入data的时刻,这一块儿就只会显现loading…,不会显现组件的细致内容

class MyComponent extends React.Component {
  render(){
    return <div>{this.props.data}</div>
  }
}

export default control(MyComponent);

HOC有什么用例?

React Redux

最典范的就是React Redux的connect要领(细致在connectAdvanced中完成)。

经由过程这个HOC要领,监听redux store,然后把下级组件须要的state(经由过程mapStateToProps猎取)和action creator(经由过程mapDispatchToProps猎取)绑定到wrappedComponent的props上。

logger和debugger

这个是官网上的一个示例,能够用来监控父级组件传入的props的转变:

function logProps(WrappedComponent) {
  return class extends React.Component {
    componentWillReceiveProps(nextProps) {
      console.log(`WrappedComponent: ${WrappedComponent.displayName}, Current props: `, this.props);
      console.log(`WrappedComponent: ${WrappedComponent.displayName}, Next props: `, nextProps);
    }
    render() {
      // Wraps the input component in a container, without mutating it. Good!
      return <WrappedComponent {...this.props} />;
    }
  }
}

页面权限治理

能够经由过程HOC对组件举行包裹,当跳转到当前页面的时刻,搜检用户是不是含有对应的权限。假如有的话,衬着页面。假如没有的话,跳转到其他页面(比方无权限页面,也许上岸页面)。

也能够给当前组件供应权限的API,页面内部也能够举行权限的逻辑推断。

底本预备把细致代码当个栗子贴出来的,效果倏忽想到公司保密协定,所以。。。

运用HOC须要注重什么?

只管不要随便修正下级组件须要的props

之所以这么说,是由于修正父级传给下级的props是有肯定风险的,可能会形成下级组件发作毛病。比方,底本须要一个name的props,然则在HOC中给删掉了,那末下级组件也许就没法一般衬着,以至报错。

Ref没法猎取你想要的ref

之前你在父组件中运用<component ref="component"/>的时刻,你能够直接经由过程this.refs.component举行猎取。然则由于这里的component经由HOC的封装,已是HOC内里的谁人component了,所以你没法猎取你想要的谁人ref(wrappedComponent的ref)。

要处理这个题目,这里有两个要领:

a) 像React Redux的connect要领一样,在内里增添一个参数,比方withRef,组件中搜检到这个flag了,就给下级组件增添一个ref,并经由过程getWrappedInstance要领猎取。

栗子:

function HOCFactory(wrappedComponent) {
  return class HOC extends React.Component {
    getWrappedInstance = ()=>{
      if(this.props.widthRef) {
        return this.wrappedInstance;
      }
    }

    setWrappedInstance = (ref)=>{
      this.wrappedInstance = ref;
    }

    render(){
      let props = {
        ...this.props
      };

      if(this.props.withRef) {
        props.ref = this.setWrappedInstance;
      }

      return <wrappedComponent {...props} />
    }
  }
}

export default HOCFactory(MyComponent);

这模样你就能够在父组件中如许猎取MyComponent的ref值了。

class ParentCompoent extends React.Component {
  doSomethingWithMyComponent(){
    let instance = this.refs.child.getWrappedInstance();
    // ....
  }

  render(){
    return <MyComponent ref="child" withRef />
  }
}

b) 另有一种要领,在官网中有提到过
父级经由过程通报一个要领,来猎取ref,细致看栗子:

先看父级组件:

class ParentCompoent extends React.Component {
  getInstance = (ref)=>{
    this.wrappedInstance = ref;
  }

  render(){
    return <MyComponent getInstance={this.getInstance} />
  }
}

HOC内里把getInstance要领看成ref的要领传入就好

function HOCFactory(wrappedComponent) {
  return class HOC extends React.Component {
    render(){
      let props = {
        ...this.props
      };

      if(typeof this.props.getInstance === "function") {
        props.ref = this.props.getInstance;
      }

      return <wrappedComponent {...props} />
    }
  }
}

export default HOCFactory(MyComponent);

谢谢@wmzy的指出,在上面的两个要领
getInstance
setWrappedInstance,由于
ES6 class的写法并不会自动绑定
this,所以
须要用bind(this)到两个要领上,确保
this的正确性。也许运用箭头函数来写两个要领,ES6的箭头函数会自动绑定
this

Component上面绑定的Static要领会丧失

比方,你本来在Component上面绑定了一些static要领MyComponent.staticMethod = o=>o。然则由于经由HOC的包裹,父级组件拿到的已不是本来的组件了,所以固然没法猎取到staticMethod要领了。

官网上的示例:

// 定义一个static要领
WrappedComponent.staticMethod = function() {/*...*/}
// 应用HOC包裹
const EnhancedComponent = enhance(WrappedComponent);

// 返回的要领没法猎取到staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true

这里有一个处理要领,就是hoist-non-react-statics组件,这个组件会自动把一切绑定在对象上的非React要领都绑定到新的对象上:

import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  hoistNonReactStatic(Enhance, WrappedComponent);
  return Enhance;
}

结束语

当你须要做React插件的时刻,HOC模子是一个很有用的模子。

愿望这篇文章能帮你对HOC有一个也许的相识和启示。

别的,这篇medium上的文章会给你更多的启示,在这篇文章中,我这里讲的被分为Props Proxy HOC,另有别的一种Inheritance Inversion HOC,强烈推荐看一看。

    原文作者:Clark
    原文地址: https://segmentfault.com/a/1190000008112017
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞