如何将装饰器语法转换为 ES6?

How to convert decorator syntax to ES6?

我正在尝试理解 some code 用 ESnext(装饰器)编写的 React。 我知道如何将装饰器从 ESnext 语法转换为 ES6 语法

// ESnext
function collect(connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
  }
}

@DragSource(Types.CARD, cardSource, collect)
export default class Card extends React.Component {
  render() {
    const { id } = this.props;
    const { isDragging, connectDragSource } = this.props;

    return connectDragSource(
      <div>
        I am a draggable card number {id}
        {isDragging && ' (and I am being dragged now)'}
      </div>
    );
  }
}

ES6

// ES6
function collect(connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
  };
}

class Card extends React.Component {
  render() {
    const { id } = this.props;
    const { isDragging, connectDragSource } = this.props;

    return connectDragSource(
      <div>
        I am a draggable card number {id}
        {isDragging && ' (and I am being dragged now)'}
      </div>
    );
  }
}

export default DragSource(Types.CARD, cardSource, collect)(Card);

但我不知道如何将此代码转换为 ES6?

function collectDrop(connect) {
  return {
    connectDropTarget: connect.dropTarget(),
  };
}

function collectDrag(connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
  };
}

@DropTarget(ItemTypes.CARD, cardTarget, collectDrop)
@DragSource(ItemTypes.CARD, cardSource, collectDrag)
export default class Card extends Component {
  static propTypes = {
    connectDragSource: PropTypes.func.isRequired,
    connectDropTarget: PropTypes.func.isRequired,
    index: PropTypes.number.isRequired,
    isDragging: PropTypes.bool.isRequired,
    id: PropTypes.any.isRequired,
    text: PropTypes.string.isRequired,
    moveCard: PropTypes.func.isRequired,
  };

  render() {
    const { text, isDragging, connectDragSource, connectDropTarget } = this.props;
    const opacity = isDragging ? 0 : 1;

    return connectDragSource(connectDropTarget(
      <div style={{ ...style, opacity }}>
        {text}
      </div>,
    ));
  }
}

因为您有两个高阶组件 (HOC) 装饰器,所以您需要将它们组合起来并在使用这两个(DropTarget 和 DragSource)导出时包装您的 class。如果您正在使用 redux 库,那么您可以使用它的效用函数 compose 组合多个 HOC 并用它包装 class。您需要关注的代码在下面代码的末尾:

import { compose } from 'redux'

function collectDrop(connect) {
  return {
    connectDropTarget: connect.dropTarget(),
  };
}

function collectDrag(connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
  };
}

class Card extends Component {
  static propTypes = {
    connectDragSource: PropTypes.func.isRequired,
    connectDropTarget: PropTypes.func.isRequired,
    index: PropTypes.number.isRequired,
    isDragging: PropTypes.bool.isRequired,
    id: PropTypes.any.isRequired,
    text: PropTypes.string.isRequired,
    moveCard: PropTypes.func.isRequired,
  };

  render() {
    const { text, isDragging, connectDragSource, connectDropTarget } = this.props;
    const opacity = isDragging ? 0 : 1;

    return connectDragSource(connectDropTarget(
      <div style={{ ...style, opacity }}>
        {text}
      </div>,
    ));
  }
}

const enhance = compose(
  DropTarget(ItemTypes.CARD, cardTarget, collectDrop),
  DragSource(ItemTypes.CARD, cardSource, collectDrag)
)

export default enhance(Card)

或者(如果你不使用 redux)你可以这样组合它们:

// Comment this part out
/* const enhance = compose(
  DropTarget(ItemTypes.CARD, cardTarget, collectDrop),
  DragSource(ItemTypes.CARD, cardSource, collectDrag)
)

export default enhance(Card)*/

// and change to this

const dropTargetHOC = DropTarget(ItemTypes.CARD, cardTarget, collectDrop)
const dragSourceHOC = DragSource(ItemTypes.CARD, cardSource, collectDrag)

export default dropTargetHOC(dragSourceHOC(Card))

TypeScript 文档提供了很好的解释 decorator composition (TS decorators and ES decorators proposal 大多数情况下是相同的):

When multiple decorators apply to a single declaration, their evaluation is similar to function composition in mathematics. In this model, when composing functions f and g, the resulting composite (f ∘ g)(x) is equivalent to f(g(x)).

As such, the following steps are performed when evaluating multiple decorators on a single declaration in TypeScript:

The expressions for each decorator are evaluated top-to-bottom.

The results are then called as functions from bottom-to-top.

所以应该是:

export default DropTarget(ItemTypes.CARD, cardTarget, collectDrop)(
  DragSource(ItemTypes.CARD, cardSource, collectDrag)(Card);
);

这应该用于学术目的,而不是用于生产。原始代码不是 ES6 而是 JSX,它仍然需要转译器(Babel)才能转换为有效 JavaScript。所以没有理由不使用 Babel 可以提供的所有功能,包括装饰器。