I'm having trouble getting react-dnd to to work. Specifically, while I can confirm dragging is being detected properly, my droppable targets are not detecting hover or drop events. I've been following the example at http://survivejs.com/react/implementing-kanban/drag-and-drop/ to create a draggable item. I've tried to use a combination of the same examples and the official examples from the official repo to create a DropTarget to accept the draggable. However, my DropTarget is giving no indication that it is detecting the draggable. My code below has multiple debugger statements to indicate if code is being reached, but none of them ever are.
I suspect that the compose
call at the end might be the problem, but I'm following Dan Abramov's example here. Just to add to the problem, the React inspector in Chrome dev tools lets me see the itemType
state variable change as I drag an item. However, both the canDrop
and isOver
state variables remain false. I'd appreciate any help to get this to work.
import { findDOMNode } from 'react-dom';
import React, { Component } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Paper from 'material-ui/Paper';
import FaDelete from 'react-icons/lib/fa/trash-o';
import RaisedButton from 'material-ui/RaisedButton';
import FaEdit from 'react-icons/lib/fa/star';
import actions from '../actions/actions';
import TextField from 'material-ui/TextField';
import { connect } from 'react-redux';
//import EmojiPickerPopup from './EmojiPickerPopup';
import RenderIf from 'render-if';
import globals from '../globals';
import { DropTarget } from 'react-dnd';
import { compose } from 'redux';
const locationItemContainer = {
display: 'flex',
flexDirection: 'column',
backgroundColor: 'lightgoldenrodyellow',
border: '1px solid Moccasin',
width: "33%",
maxHeight: "15em"
}
const controlsContainer = {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-around',
width: "100%"
}
const paperStyle = {
padding: '8px 4px',
display: 'flex',
flexDirection: "column",
alignItems: 'center',
justifyContent: 'center',
width: "100%",
height: "100%"
};
class LocationItemComponent extends Component {
constructor(props, context) {
super(props, context);
this.state = {
locationMarkers: []
}
}
componentWillReceiveProps(nextProps) {
if (!this.props.isOver && nextProps.isOver) {
// You can use this as enter handler
debugger
}
if (this.props.isOver && !nextProps.isOver) {
// You can use this as leave handler
debugger
}
if (this.props.isOverCurrent && !nextProps.isOverCurrent) {
// You can be more specific and track enter/leave
// shallowly, not including nested targets
debugger
}
}
nameChanged = (id, event, value) => {
this.props.dispatch(actions.storyMapActions.updateMarkerName(value, id));
}
deleteMarker = (id) => {
this.props.dispatch(actions.storyMapActions.deleteMarker(id));
}
showEmojiPicker = (id, event) => {
this.props.dispatch(actions.modalsActions.showEmojiPicker(id, event.currentTarget))
}
render() {
const { isOver, canDrop, connectDropTarget } = this.props;
if (isOver) {
console.log("is over");
}
return connectDropTarget(
<div style={locationItemContainer}>
<MuiThemeProvider>
<Paper zDepth={5}
style={paperStyle}
rounded={false}>
<TextField
id="markerName"
hintText="marker Name"
onChange={this.nameChanged.bind(this, this.props.marker.id)}
value={this.props.marker.name}
underlineFocusStyle={{ color: globals.textUnderlineColor }}
/>
<div style={controlsContainer}>
<RaisedButton
icon={<FaEdit />}
primary={true}
onClick={this.showEmojiPicker.bind(this, this.props.marker.id)} />
<RaisedButton
icon={<FaDelete />}
secondary={true}
onClick={this.deleteMarker.bind(this, this.props.marker.id)} />
</div>
</Paper>
</MuiThemeProvider>
</div>
);
}
}
const mapStateToProps = (state) => {
return Object.assign({}, { state: state });
}
const locationTarget = {
canDrop(props, monitor) {
debugger;
// You can disallow drop based on props or item
const item = monitor.getItem();
return true;
},
hover(props, monitor, component) {
debugger;
// This is fired very often and lets you perform side effects
// in response to the hover. You can't handle enter and leave
// hereāif you need them, put monitor.isOver() into collect() so you
// can just use componentWillReceiveProps() to handle enter/leave.
// You can access the coordinates if you need them
const clientOffset = monitor.getClientOffset();
const componentRect = findDOMNode(component).getBoundingClientRect();
// You can check whether we're over a nested drop target
const isJustOverThisOne = monitor.isOver({ shallow: true });
// You will receive hover() even for items for which canDrop() is false
const canDrop = monitor.canDrop();
},
drop(props, monitor, component) {
debugger;
if (monitor.didDrop()) {
// If you want, you can check whether some nested
// target already handled drop
debugger
return;
}
// Obtain the dragged item
const item = monitor.getItem();
// You can do something with it
//ChessActions.movePiece(item.fromPosition, props.position);
// You can also do nothing and return a drop result,
// which will be available as monitor.getDropResult()
// in the drag source's endDrag() method
return { moved: true };
}
};
const collect = (connect, monitor) => {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
itemType: monitor.getItemType()
};
}
export default compose(
connect(mapStateToProps),
DropTarget(globals.itemTypes.LOCATION_ITEM, locationTarget, collect)
)(LocationItemComponent);