ES 6 裝飾器與 React 高階元件

Russ_Zhong發表於2018-12-07

關於 Decorator 到底是 ES 6 引入的還是 ES 7 引入的我也不是很明白了,兩種說法都有,這種問題懶得糾結了……在用的時候發現這個東西很好用,平常用處可能不大,但是結合 React 就很好使了。接下來就講一講。

一、環境搭建

我搭建了一個 React 開發環境,結合 babel 的外掛——babel-plugin-transform-decorators-legacy一起使用,這個外掛可以讓你寫 Decorator。

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

可以通過如下命令克隆:

$ git clone https://github.com/zhongdeming428/HOC.git
複製程式碼

克隆下來以後就可以嘗試啦!

二、Decorator 的基本使用

裝飾器本身就是一個函式,使用起來挺簡單,無非就是修飾類或者類的函式。使用 @ 呼叫,扔在要修飾的類或者類方法前面就可以了。但是在修飾類和類函式的時候又有細微的差異。

class A {
    @sayB
    sayA() {
        console.log('a');
    }
}
function sayB(target, name, descriptor) {
    // ...
}
複製程式碼

在使用裝飾器裝飾類函式的時候,可以接受三個引數。第一個是要修飾的物件,第二個是修飾的屬性名,第三個是屬性描述符。可以在我搭建的專案中進行嘗試。

在用裝飾器裝飾類的時候,只能夠接受一個引數——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);
複製程式碼

結果會輸出 AB

這就就可以用同一個裝飾器實現不同行為的裝飾了。

那麼結合 React 有什麼妙用呢?

三、結合 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 程式碼!

相關文章