React高阶组件-总结

React高阶组件

索引

在React组件的构建过程当中,经常有这样的场景,有一类功能须要被不一样的组件公用,此时,就涉及抽象的话题,在不一样设计理念下,有许多的抽象方法,而针对React,咱们重点讨论两种:mixin和高阶组件。

Mixin

mixin的特性一直普遍存在于各类面向对象语言中。好比在Ruby中,include关键词便是mixin。是将一个模块混入到一个另外一个模块中,或是一个类中。为何编程语言要引入这样一种特性呢?事实上,包括C++等一些年龄较大的OOP语言,它们都有一个强大但危险的多重继承特性。在现代语言中,为了权衡利弊,大都舍弃了多重继承,只采用单继承,但单继承在实现抽象时有不少不方便的地方,为了弥补缺失,java引入了接口interface。其余一些语言则引入了像mixin的技巧。javascript

alt text

Mixin的问题

从上面的代码,咱们不难看出,对于广义的mixin方法,就是用赋值的方式将mixin对象里的方法都挂载到原对象上,来实现对对象的混入。java

从上述的实现,咱们能够联想到 underscore库中的extend 或 lodash库中的 assign方法,或者说ES6中的Object.assign()方法。react

React官方已不推荐使用Mixins的技术来实现代码的重用,Mixins技术有一系列的缺点,首先Mixins会形成命名冲突,若是你须要注入多个mixins,其中一个是本身的,另外的多是第三方的。那有可能在两个mixins里使用了相同名称的方法,这会使得其中的一个不起做用,而你能作的只有修改其中一个方法的名称。另外一方面,一个mixins一开始多是很是简单的,仅仅须要实现某一个功能,但当业务越加的复杂,须要往其中加入更多的方法的时候,就会变得很是复杂。针对这些困扰,React社区提出来新的方式来取代mixin,那就是高阶组件。编程

概念

说到高阶组件,就先得说到高阶函数了,高阶函数是至少知足下列条件的函数在javascript这门函数为一等公民的语言中,高阶函数的使用仍是很是之多的,像咱们平时的回调函数等等,都用到了高阶函数的知识。咱们先来看一个简单的高阶函数redux

alt text

从上面的代码,咱们不难看出,对于广义的mixin方法,就是用赋值的方式将mixin对象里的方法都挂载到原对象上,来实现对对象的混入。从上述的实现,咱们能够联想到 underscore库中的extend 或 lodash库中的 assign方法,或者说ES6中的Object.assign()方法。设计模式

高阶组件实际上是差很少的用法,类比高阶函数的定义,高阶组件就是接受一个组件做为参数,在函数中对组件作一系列的处理,随后返回一个新的组件做为返回值。高阶组件(HOC)是 React 中用于重用组件逻辑的高级技术。 HOC 自己不是 React API 的一部分。 它们是从 React 构思本质中浮现出来的一种模式,这种模式是由React自身的组合性质必然产生的。具体来讲,高阶组件是一个函数,可以接受一个组件做为参数,在函数中对组件作一系列的处理,随后返回一个新的组件做为返回值。在咱们项目中使用react-redux框架的时候,有一个connect的概念,这里的connect其实就是一个高阶组件。也包括相似react-router-dom中的withRouter的概念。babel

装饰器模式

先来一个最简单的demo

alt text

alt text

组件Usual经过simpleHoc的包装,打了一个log... 那么形如simpleHoc就是一个高阶组件了,经过接收一个组件class Usual,并返回一个组件class。 其实咱们能够看到,在这个函数里,咱们能够作不少操做。 并且return的组件一样有本身的生命周期,function,另外,咱们看到也能够把props传给WrappedComponent(被包装的组件)。markdown

高阶组件能够看作是装饰器模式(Decorator Pattern)在React的实现。即容许向一个现有的对象添加新的功能,同时又不改变其结构,属于包装模式(Wrapper Pattern)的一种ES7中添加了一个decorator的属性,使用@符表示,能够更精简的书写。那上面的例子就能够改为 是一样的效果。固然兼容性是存在问题的,一般都是经过babel去编译的。 babel提供了plugin,transform-decorators-legacy。react-router

alt text

实现方式

实现高阶组件的方法有以下两种:属性代理、反向继承。app

属性代理:高阶组件经过被包裹的React组件来操做props

属性代理有以下4点常见做用:

  1. 操做props

  2. 经过refs访问组件实例

  3. 提取state

  4. 用其余元素包裹WrappedComponent,实现布局等目的

alt text

alt text

alt text

alt text

能够经过传入 props 和回调函数把 state 提取出来使用ppHOC装饰器以后,组件的props被添加了name属性,能够经过下面的方法,将value和onChange添加到input上面 input会成为受控组件

反向继承:高阶组件继承于被包裹的React组件。

反向继承能够劫持被继承class的render内容,进行修改,过滤后,返回新的显示内容。 之因此被称为渲染劫持是由于 HOC 控制着 WrappedComponent 的渲染输出,能够用它作各类各样的事。

alt text

alt text

如上代码。高阶组件返回的组件继承于 WrappedComponent 。由于被动地继承了 WrappedComponent,全部的调用都会反向,这也是种方法的由来。这种方法与属性代理不太同样。它经过继承WrappedComponent来实现,方法能够经过super来顺序调用。由于依赖于继承机制。HOC的调用顺序和队列是同样的。在由 render输出的任何 React 元素中读取、添加、编辑、删除 props 读取和修改由 render 输出的 React 元素树 有条件地渲染元素树 把样式包裹进元素树,就行Props Proxy那样包裹其余的元素

应用场景

一、两个页面UI几乎同样,功能几乎相同,仅仅几个操做不太同样,却写了两个耦合不少的页面级组件

二、以前写过一个组件A,作完上线,以后加了一个新需求,很奇怪要作的组件B跟A几乎如出一辙,但稍微有区别

三、Container解决不了的时候甚至不太优雅的时候

四、实现一个从loaclstorage返回记录的功能

五、Mobx-react、react-redux

A).Container解决不了的时候甚至不太优雅的时候。其实大部分时候包一层Container组件也能作到差很少的效果,好比操做props,渲染劫持。但其实仍是有很大区别的。好比咱们如今有两个功能的container,添加样式和添加处理函数的,对Usual进行包装.

B).mobx-react就是高阶组件是一个实际应用@observer装饰器将组件包装为高阶组件,传入组件MyComponent后,mobx-react会对其生命周期进行各类处理,并经过调用forceUpdate来进行刷新实现最小粒度的渲染。mobx提倡一份数据引用,而redux中则提倡immutable思想,每次返回新对象。

C).react-redux中的connect

D).connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])(WrappedComponent)

例如,咱们把组件ComponentA链接到Redux上的写法相似于:

const ConnectedComponentA = connect(mapStateToProps,mapDispatchToProps)(ComponentA);

咱们能够把它拆分来看:

// connect 是一个函数,返回值enhance也是一个函数

const enhance = connect(mapStateToProps, mapDispatchToProps);

// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store关联起来的新组件

const ConnectedComponentA = enhance(ComponentA);

connect这个函数会将一个React组件链接到Redux 的 store

这个函数会将一个React组件链接到Redux 的 store。在链接的过程当中,connect经过参数 mapStateToProps,从全局store中取出当前组件须要的state,并把state转化成当前组件的props;同时经过参数 mapDispatchToProps,把当前组件用到的Redux的action creators,以props的方式传递给当前组件。

总结

一、高阶组件不会修改子组件,也不拷贝子组件的行为,建议使用组合。

二、要给hoc添加class名,便于debugger。

三、静态方法要复制。

四、refs不会传递。

五、不要在render方法内部使用高阶组件

高阶组件是react应用中很重要的一部分,最大的特色就是重用组件逻辑,提升组件的解耦和灵活性,它并非由React API定义出来的功能,而是由React的组合特性衍生出来的一种设计模式。若是你用过redux,那你就必定接触太高阶组件,由于react-redux中的connect就是一个高阶组件。