2
import React from 'react';
import { findDOMNode } from 'react-dom';
import { DropTarget } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';


let newStyle = {'display':'none','left':'0px'} ;

let target = {
    hover(props,monitor,component){
        newStyle.display = 'block';
        newStyle.left = monitor.getClientOffset().x-findDOMNode(component).getBoundingClientRect().left+'px';
        //The current mouse position where the "on hover indicator" is expected

        return;
    },
    drop(props, monitor,component) {
        newStyle.display = 'none';
        newStyle.left = '0px';
        return props;
    }
}

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

class component extends React.Component { 
    constructor(props){
        super(props);
    }

    render = () => { 
        const { connectDropTarget } = this.props;
        return connectDropTarget(
            <div>
              <Span style = { newStyle }> On hover indicator </span>
              // here another component drops wrapped within div!
            </div>
        )
    }
}

export default DropTarget('type', target, collect)(component);

In hover callback if I log my left property of newStyle object it displays the current mouse position as expected but it does not change the style of the span in the render method.

Any help would be appreciated. Thanks in advance.

Neha AB
  • 60
  • 1
  • 9

2 Answers2

2

Just changing the value of a variable used in React won't force a rerender - just changing the value of newStyle won't do anything. To get a React component to rerender itself you need to either a) Call setState or b) Call forceUpdate.

What you could do instead to make it update with the new style on hover would be to add it to the state, and then manipulate that state within the hover function, maybe something like this:

import React from 'react';
import { findDOMNode } from 'react-dom';
import { DropTarget } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';

let target = {
    hover(props,monitor,component) {
        let newStyle = {};
        newStyle.display = 'block';
        newStyle.left = monitor.getClientOffset().x-findDOMNode(component).getBoundingClientRect().left+'px';

        component.setState({ style: newStyle });
        return;
    },
    drop(props, monitor,component) {
        let newStyle = {}
        newStyle.display = 'none';
        newStyle.left = '0px';

        component.setState({ style: newStyle });
        return props;
    }
}

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

class component extends React.Component { 
    constructor(props){
        super(props);
    }

    state = {
      style: { display: 'none', left: '0px' }
    }

    render = () => { 
        const { connectDropTarget } = this.props;
        return connectDropTarget(
            <div>
              <Span style={ this.state.style }> On hover indicator </span>
              // here another component drops wrapped within div!
            </div>
        )
    }
}

export default DropTarget('type', target, collect)(component);

Note the component.setState() within both the hover and drop functions, this will actually force the component to re-render. 'component' in this instance is actually a reference to the instance of the component, so you have access to it's state from that as well if you need to read the state to do anything else. Check out this section of React's lifecycle docs if you want to get more of an idea what you were doing wrong: https://facebook.github.io/react/docs/react-component.html#setstate

Ben Hare
  • 4,365
  • 5
  • 27
  • 44
  • Thanks @BenHare for your help, I got my mistake. And yes when I forcefully update it continuously, It re-renders component as long as I keep dragging my another component so it overflows the stack. Is there any another way to make it better? – Neha AB Aug 21 '17 at 09:45
  • What do you mean by overflows the stack? I don't see a stack in your implementation at all. If you mean, is there a way to make it continuously changing without continuously updating, then no, there isn't. React only changes components on a rerender. But I wouldn't worry about that too much - it's usually not expensive enough performance wise to worry about it. – Ben Hare Aug 21 '17 at 11:38
  • No, actually I made another mistake of passing the newStyle object as a prop to another react component. Thanks for the help!:) – Neha AB Aug 21 '17 at 13:58
  • @BenHare actually I'm looking for a solution something similar to this, can you please step by since i'm facing hard time to get this done https://stackoverflow.com/questions/58555543/react-dnd-make-droptargets-when-hover-on-something-while-dragging-item. – Learn to code Oct 29 '19 at 07:23
0

This worked for me: Instead of using monitor.isDragging() to set the isDragging state use -> !!monitor.getItem(), like so:

const [{ opacity, isDragging }, drag] = useDrag({
    type: itemType,
    item: () => originalItem,
    canDrag,
    collect: (monitor: DragSourceMonitor) => ({
      opacity: monitor.isDragging() ? 0 : 1,
      isDragging: !!monitor.getItem()
    })
  });

That way the isDragging state will be false whenever no dragging is occurring and true when an item is dragged.

and the hover styles will be applied only when isDragging is false:

 <Box
      ref={ref}
      sx={{
        opacity,
        ...(!isDragging && {
            '&:hover': {
              borderRadius: '4px',
              border: `1px solid blue`
            }
          })
      }}
      data-handler-id={handlerId}
    >
....
</Box>
Daniel Bellmas
  • 589
  • 5
  • 17