Reputation: 534
I am trying to understand some code React written in ESnext (decorators). I know how to convert decorators from ESnext to ES6 syntax
// 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);
But I'm stuck how to convert this code to 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>,
));
}
}
Upvotes: 2
Views: 1587
Reputation: 222354
TypeScript documentation provides good explanation of decorator composition (TS decorators and ES decorators proposal are identical for the most part):
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.
So it is supposed to be:
export default DropTarget(ItemTypes.CARD, cardTarget, collectDrop)(
DragSource(ItemTypes.CARD, cardSource, collectDrag)(Card);
);
This is supposed to be used for academical purposes and not in production. The original code is not ES6 but JSX, it still requires a transpiler (Babel) to be converted to valid JavaScript. So there's no reason to not use all features that Babel can offer, including decorators.
Upvotes: 1
Reputation: 3627
Because you have two Higher Order Component (HOC) decorators you need to combine them and wrap your class when exporting with these two (DropTarget and DragSource). If you're using redux
library then you can use its utility function compose
that combines multiple HOCs and wraps the class with it. The code you need to focus on is at the end of the code below:
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)
Or (if you're not using redux) you can combine them like that:
// 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))
Upvotes: 1