资讯专栏INFORMATION COLUMN

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

Leo_chen / 995人阅读

摘要:栗子的方法就是一个,他获取,在中给添加需要的。本来准备把详细代码当个栗子贴出来的,结果突然想到公司保密协议,所以。。。栗子这样子你就可以在父组件中这样获取的值了。

什么是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 
    }
  }
}
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 
    }
  }
}

这样,你就可以在你的组件中使用message这个props:

class MyComponent extends React.Component {
  render(){
    return 
{this.props.message}
} } export default control(MyComponent);
渲染劫持

这里的渲染劫持并不是你能控制它渲染的细节,而是控制是否去渲染。由于细节属于组件内部的render方法控制,所以你无法控制渲染细节。

比如,组件要在data没有加载完的时候,现实loading...,就可以这么写:

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

这个样子,在父级没有传入data的时候,这一块儿就只会显示loading...,不会显示组件的具体内容

class MyComponent extends React.Component {
  render(){
    return 
{this.props.data}
} } 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 ;
    }
  }
}
页面权限管理

可以通过HOC对组件进行包裹,当跳转到当前页面的时候,检查用户是否含有对应的权限。如果有的话,渲染页面。如果没有的话,跳转到其他页面(比如无权限页面,或者登陆页面)。

也可以给当前组件提供权限的API,页面内部也可以进行权限的逻辑判断。

本来准备把详细代码当个栗子贴出来的,结果突然想到公司保密协议,所以。。。

使用HOC需要注意什么? 尽量不要随意修改下级组件需要的props

之所以这么说,是因为修改父级传给下级的props是有一定风险的,可能会造成下级组件发生错误。比如,原本需要一个name的props,但是在HOC中给删掉了,那么下级组件或许就无法正常渲染,甚至报错。

Ref无法获取你想要的ref

以前你在父组件中使用的时候,你可以直接通过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 
    }
  }
}

export default HOCFactory(MyComponent);

这样子你就可以在父组件中这样获取MyComponent的ref值了。

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

  render(){
    return 
  }
}

b) 还有一种方法,在官网中有提到过:
父级通过传递一个方法,来获取ref,具体看栗子:

先看父级组件:

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

  render(){
    return 
  }
}

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 
    }
  }
}

export default HOCFactory(MyComponent);
感谢@wmzy的指出,在上面的两个方法getInstancesetWrappedInstance,由于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,强烈推荐看一看。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81199.html

相关文章

  • React 高阶组件HOC实践

    摘要:简单来说高阶组件就是一个函数,它接受一个组件作为参数然后返回一个新组件。主要用于组件之间逻辑复用。使用由于数据请求是异步的,为了不让用户看到一片空白,当数据请求还没有返回时,展示组件。组合函数,提升代码可阅读性。 简单来说高阶组件(HOC)就是一个函数,它接受一个组件作为参数然后返回一个新组件。HOC 主要用于组件之间逻辑复用。比如你写了几个组件,他们之间的逻辑几乎相同,就可以用 HOC 对...

    caiyongji 评论0 收藏0
  • 【译】TypeScript中的React高阶组件

    摘要:原文链接高阶组件在中是组件复用的一个强大工具。在本文中,高阶组件将会被分为两种基本模式,我们将其命名为和用附加的功能来包裹组件。这里我们使用泛型表示传递到的组件的。在这里,我们定义从返回的组件,并指定该组件将包括传入组件的和的。 原文链接:https://medium.com/@jrwebdev/... 高阶组件(HOCs)在React中是组件复用的一个强大工具。但是,经常有开发者在...

    wizChen 评论0 收藏0
  • 基于Decorator的React高阶组件的思路分析实现

    摘要:在深入技术栈一书中,提到了基于的。书里对基于的没有给出完整的实现,在这里实现并记录一下实现的思路。在这里最小的组件就是。对比范式与父组件的范式,如果完全利用来实现的,将操作与分离,也未尝不可,但却不优雅。 在深入react 技术栈一书中,提到了基于Decorator的HOC。而不是直接通过父组件来逐层传递props,因为当业务逻辑越来越复杂的时候,props的传递和维护也将变得困难且冗...

    LinkedME2016 评论0 收藏0
  • React-sortable-hoc 结合 hook 实现 Draggin 和 Droppin

    摘要:启动项目教程最终的目的是构建一个带有趣的应用程序来自,可以在视口周围拖动。创建组件,添加样式和数据为简单起见,我们将在文件中编写所有样式。可以看出,就是在当前的外层包裹我们所需要实现的功能。现在已经知道如何在项目中实现拖放 翻译:https://css-tricks.com/draggi... React 社区提供了许多的库来实现拖放的功能,例如 react-dnd, react-b...

    molyzzx 评论0 收藏0
  • React高阶组件

    摘要:高阶组件高阶函数接收函数作为输入,或者输出另一个函数的一类函数高阶组件接收组件作为输入,输出一个新的组件的组件。包含了一系列实用的高阶组件库拖动库深入理解高阶组件其中详细介绍了属性代理和反向继承的区别。 React高阶组件 高阶函数: 接收函数作为输入,或者输出另一个函数的一类函数; 高阶组件: 接收React组件作为输入,输出一个新的React组件的组件。 高阶组件通过包裹一个新传入...

    cncoder 评论0 收藏0

发表评论

0条评论

Leo_chen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<