ES 6 装饰器与 React 高阶组件

关于 Decorator 究竟是 ES 6 引入的仍是 ES 7 引入的我也不是很明白了,两种说法都有,这种问题懒得纠结了……在用的时候发现这个东西很好用,日常用处可能不大,可是结合 React 就很好使了。接下来就讲一讲。react

1、环境搭建

我搭建了一个 React 开发环境,结合 babel 的插件——babel-plugin-transform-decorators-legacy一块儿使用,这个插件可让你写 Decorator。git

GitHub 地址:https://github.com/zhongdeming428/HOCgithub

能够经过以下命令克隆:redux

$ git clone https://github.com/zhongdeming428/HOC.git
复制代码

克隆下来之后就能够尝试啦!bash

2、Decorator 的基本使用

装饰器自己就是一个函数,使用起来挺简单,无非就是修饰类或者类的函数。使用 @ 调用,扔在要修饰的类或者类方法前面就能够了。可是在修饰类和类函数的时候又有细微的差别。babel

class A {
    @sayB
    sayA() {
        console.log('a');
    }
}
function sayB(target, name, descriptor) {
    // ...
}
复制代码

在使用装饰器装饰类函数的时候,能够接受三个参数。第一个是要修饰的对象,第二个是修饰的属性名,第三个是属性描述符。能够在我搭建的项目中进行尝试。markdown

在用装饰器装饰类的时候,只可以接受一个参数——target。这区别于上面的状况:函数

@APlus
class A {

}
function APlus(target, name, descriptor) {
    // ... 打印一下能够发现 name、descriptor 是 undefined。
}
复制代码

另外,装饰器还能够接受参数,返回一个符合装饰器规范的新函数便可,这样又能够对装饰器的装饰行为进行定制了。好比:优化

@attach2Prop({ name: 'A' })
class A {

}

@attach2Prop({ name: 'B' })
class B {

}

function attach2Prop(obj) {
    return function(target) {
        target.prototype.$data = obj;
    }
}

console.log((new A()).$data.name);
console.log((new B()).$data.name);
复制代码

结果会输出 ABspa

这就就能够用同一个装饰器实现不一样行为的装饰了。

那么结合 React 有什么妙用呢?

3、结合 React 使用

(1)简化 React-Redux 的使用

以往在使用 react-redux 时,在定义好 UI 组件后,还要定义容器组件:

class UIComponent extends React.Component {

}

const ContainerComponent = connect(mapState2Props, mapDispatch2Props)(UIComponent);

export default ContainerComponent;
复制代码

有了装饰器以后:

@connect(mapState2Props, mapDispatch2Props)
class UIComponent extends React.Component {

}

export default UIComponent;
复制代码

这样用简化的代码达到了一样的效果,还省去了给容器组件命名的麻烦……代码也更加的整洁。

(2)定制高阶组件

上一小节中的容器组件实际上就是一个高阶组件,可是咱们本身有时候也要定义一些高阶组件,实现代码的更高层次的复用。

例如:咱们作了一个组件库,里面有一部分的组件是有一个功能特征的,那就是能够拖拽;又好比咱们作的移动端组件,须要实现一个左滑删除功能。咱们须要给每种具备这个特征的组件写一遍拖拽或者左滑删除逻辑吗?

显然是否认的,咱们能够实现一个纯逻辑组件,而非 UI 组件,它的功能就是使得你的 UI 组件具备某种特定功能。好比上面提到的左滑删除或者拖拽。

这个纯逻辑组件就能够是一个装饰器,是一个高阶组件。

在我搭建的开发环境中,就实现了这样一个简单的高阶组件,让你的 UI 组件在鼠标滑入时显示为一只手。

装饰器代码以下:

// src/decorators/CursorPointer.js
import React from 'react';

export default Component => class extends React.Component {
  render() {
    return <div style={{cursor: 'pointer', display: 'inline-block'}}> <Component/> </div>
  }
}
复制代码

这个装饰器(高阶组件)接受一个 React 组件做为参数,而后返回一个新的 React 组件。实现很简单,就是包裹了一层 div,添加了一个 style,就这么简单。之后全部被它装饰的组件都会具备这个特征。

使用这个装饰器:

import React from 'react';
import Clickable from '../decorators/CursorPointer';

@Clickable
class ClickablePanel extends React.Component {
  render() {
    return <div className="panel"> </div>
  }
}

export default ClickablePanel;
复制代码

将装饰器与高阶组件相结合,能够大大优化你的 React 代码!