I've integrated the React DND library into my React + Redux application but I'm having some troubles troubleshooting an error that happens when a DragSource leaves a DropTarget.
The problem is: when I drag a component over a target it receives correctly the isOver variable on entering but when I move the mouse out leaving that target, and I move over another target, they both keep the isOver variable as TRUE even tho only the last target needs to have TRUE as isOver.
The strange thing is when I drop the source component on the correct target, only the correct target receives the item. Same when I drop the source component on a blank part of the page, the isOver resets correctly so this error happens only while keep dragging.
Here's my root application file:
// Libraries
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "./store/index.js";
import { router } from "./router/index.js";
import { DragDropContextProvider } from 'react-dnd';
import MultiBackend from 'react-dnd-multi-backend';
import HTML5toTouch from 'react-dnd-multi-backend/lib/HTML5toTouch';
// Render
ReactDOM.render(
<Provider store={store}>
<DragDropContextProvider backend={MultiBackend(HTML5toTouch)}>
{router}
</DragDropContextProvider>
</Provider>,
document.getElementById('app')
);
Here's the DragSource component:
// Libraries
import React from "react";
import {connect} from "react-redux";
import {compose, bindActionCreators} from 'redux';
import { DragSource } from 'react-dnd';
// Actions
import * as SystemActions from 'controllers/core/system/actions';
import * as PublisherActions from 'sections/publisher/store/actions';
// States
import mapStateToProps from 'controllers/states';
// DND Type
const Types = {
CONTACT: 'contact'
};
// DND Source
const contactSource = {
isDragging(props, monitor) {
return monitor.getItem().id === props.Contact.id;
},
beginDrag(props, monitor, component) {
const item = {
id: props.Contact.id
};
return item;
},
endDrag(props, monitor, component) {
if (!monitor.didDrop()) {
return;
}
const item = monitor.getItem();
const dropResult = monitor.getDropResult();
}
};
// DND Collect
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
};
}
export class ContactPreview extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// Render
render() {
const {Contact, isOver, isOverCurrent, canDrop, connectDragSource} = this.props;
// Return
return connectDragSource(
<div className="contact_element">
{Contact.name}
</div>
);
}
}
function mapDispatchToProps(dispatch){
return{
SystemActions: bindActionCreators(SystemActions, dispatch),
PublisherActions: bindActionCreators(PublisherActions, dispatch)
}
}
export default compose(
DragSource(Types.CONTACT, contactSource, collect),
connect(mapStateToProps, mapDispatchToProps)
)(ContactPreview);
Here's DropTarget which is contained in a MAP loop:
// Libraries
import React from "react";
import {connect} from "react-redux";
import {compose, bindActionCreators} from 'redux';
import { DropTarget } from 'react-dnd';
// Actions
import * as SystemActions from 'controllers/core/system/actions';
import * as PublisherActions from 'sections/publisher/store/actions';
// States
import mapStateToProps from 'controllers/states';
// DND Types
const Types = {
CONTACT: 'contact'
};
// DND Collect
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
isOverCurrent: monitor.isOver({ shallow: true }),
canDrop: monitor.canDrop(),
itemType: monitor.getItemType()
};
}
// DND Target
const tagTarget = {
drop(props, monitor, component) {
let ComponentProps = component.selector.props;
const item = monitor.getItem();
ComponentProps.PublisherActions.addTagContact('tag_preview', component.props.Tag.id, item.id);
}
};
// Module
export class TagPreview extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const {Tag, isOver, isOverCurrent, canDrop, connectDropTarget} = this.props;
return connectDropTarget(
<div className="tag_element" id={isOverCurrent ? "over":""}>
{Tag.name}
</div>
);
}
}
function mapDispatchToProps(dispatch){
return{
SystemActions: bindActionCreators(SystemActions, dispatch),
PublisherActions: bindActionCreators(PublisherActions, dispatch)
}
}
export default compose(
DropTarget(Types.CONTACT, tagTarget, collect),
connect(mapStateToProps, mapDispatchToProps)
)(TagPreview);
You can see the problem with this image:
All targets keeps isOver as TRUE even if the source has left
If I drag the contact over all the targets (in this case the Tags) every tag keep the isOver prop as TRUE even if I've left the target and it resets only if I drop the source.