0

I am trying to implement drag and drop in react and if I call setState in the onDragOver handler (to be able to do some styling based on the drag state), onDrop will not be called on the second drop. If I remove the call to setState it works fine. What puzzles me is that it works the first time. I would expect that if it doesn't work it just wouldn't work at all.

Note: I am using react this with styled components.

I have a class based component as follows:

  state = {
    status: STATUS.NOT_DRAGGING,
  };

  onDragOver = (e) => {
    e.preventDefault();
    // this line is causing all the trouble
    // this.setState(() => ({ status: STATUS.DRAGGING }));
  };

  onDrop = (e, rowId) => {
    // get the item being dragged using dto
    const id = e.dataTransfer.getData('id');
    const name = e.dataTransfer.getData('name');

    // clear out the data transfer array
    e.dataTransfer.clearData();

    if (id === null || id === 'undefined') return;

    const node = { id, name };
    const settings = this.props.getPlotContainer(rowId).settings;

    // add node to plot
    this.props.addNodeToPlot(rowId, node, settings);

    this.setState({ status: STATUS.DROPPED });
  };

and my droppable div:

      <MainContainer
        status={this.state.status}
        className="droppable"
        onDragOver={e => this.onDragOver(e)}
        onDrop={e => this.onDrop(e, rowId)}
      >
        {(plot && plot.nodes.length === 0) && <EmptyPlot status={this.state.status}>{message}</EmptyPlot>}
        {(plot && plot.nodes.length > 0) && this.renderPlotSettings(plot, rowId)}
        {(plot && plot.nodes.length > 0) && this.renderPlot(plot)}
        {(plot && plot.nodes.length > 0) && this.renderSeriesSettings(plot, rowId)}
      </MainContainer>

Any advice or tips greatly appreciated!

cbutler
  • 1,111
  • 5
  • 16
  • 32
  • I think I have an idea what is happening. On the second drop, there are child elements inside the droppable container so according to the question below on stack overflow I need to climb upwards in the dom tree to the 'MainContainer' (the div with the drop event handler). https://stackoverflow.com/questions/28203585/prevent-drop-inside-a-child-element-when-drag-dropping-with-js – cbutler May 21 '19 at 06:27

1 Answers1

1

I'm providing my solution in case anyone else runs into this problem.

The best work around I could come up with was to move setting the drag state from the droppable element's onDragOver to the draggable element's onDragStart and onDragEnd events. Since these elements are in different components I moved the state out of the component to redux.

As a note, I would also avoid setting state or firing off actions in events like onDrag, onDragOver, etc. that are 'continuous' for a unknown period of time.

Hope this helps someone else out.

cbutler
  • 1,111
  • 5
  • 16
  • 32