Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use a third-party component as a drag source? #347

Closed
DanLipsitt opened this issue Dec 18, 2015 · 9 comments
Closed

How to use a third-party component as a drag source? #347

DanLipsitt opened this issue Dec 18, 2015 · 9 comments

Comments

@DanLipsitt
Copy link

I want to use a third-party component (in this case a <ListGroupItem> from React Bootstrap) as a drag source. When I call connectReactSource on it I get an error telling me

Only native element nodes can now be passed to connectDragSource(). You can either wrap ListGroupItem into a

, or turn it into a drag source or a drop target itself.

However, if I wrap it in a <div>, it will mess up my markup and styling because it is supposed to be a <li>. The other option is to turn it into a drag source, but I don't see how to do that without editing the source of the third-party component. What is the right thing to do here?

@gaearon
Copy link
Member

gaearon commented Dec 19, 2015

Good question. I think you should be able to do this:

import { findDOMNode } from 'react-dom';

class DraggableListGroupItem extends Component {
  render() {
    const { connectDragSource, ...rest } = this.props;
    return (
      <ListGroupItem
        {...rest}
        ref={instance => connectDragSource(findDOMNode(instance))}
      />
    );
  }
}

export default DragSource(...)(DraggableListGroupItem);

This works because connect*() functions look at what you pass them. They can either be used declaratively (pass a DOM React element and get a DOM React element in return) or imperatively (call them with DOM node and don't forget to call them with null later—just like refs work).

@gaearon gaearon closed this as completed Dec 19, 2015
@gaearon
Copy link
Member

gaearon commented Dec 19, 2015

(This also gives you the freedom to use some other DOM element as the drag source—for example, you can query the node you get from findDOMNode(). This should be used as last resort because it's bad to rely on internal DOM structure of a third-party component.)

@DanLipsitt
Copy link
Author

This didn't work for me. The nesting is now correct but the element is not draggable and I get the following error in the console:

Unhandled promise rejection TypeError: (0 , _reactDom2.default) is not a function(…)

@gaearon
Copy link
Member

gaearon commented Dec 29, 2015

I believe you typed

import findDOMNode from 'react-dom';

rather than

import { findDOMNode } from 'react-dom';

Indeed react-dom doesn't have a default export, that's why I used a specific named export called findDOMNode. More on difference between default and named exports: http://exploringjs.com/es6/ch_modules.html

@DanLipsitt
Copy link
Author

I was in the middle of trying to write a reduced example but you caught the bug first!

I know about the difference between default and named exports but I don't always notice which one I need and the debug tools aren't great at giving an error message that shows what's going on. I can see now how that error message points at the problem even though it's been mangled by Webpack. Thanks for the lesson!

@DanLipsitt
Copy link
Author

PS: if I were to file a pull request to add this to the docs (not sure I'll have time), where would I put it? It would help if the connectDragSource error pointed to this solution.

@gaearon
Copy link
Member

gaearon commented Dec 29, 2015

Not sure, I think the current message is good enough. Now we have this issue for googling too :-)

By the way I re-read your question and want to make a point that you absolutely don't have to wrap it in <div> specifically. The point of the error message is that connectDragSource() needs to wrap any DOM element—<div>, <li>, or anything else. You only need the workaround above for custom components.

@mathieumg
Copy link

Now we have this issue for googling too :-)

Fortunately! 😁

Just want to append a little precision, if you're used to chain the connect*() calls in your render()s, the same won't work with the above approach, instead you need to do the following:

ref={instance => {
  const node = findDOMNode(instance);
  connectDragSource(node);
  connectDropTarget(node);
}}

@megarg
Copy link

megarg commented Aug 5, 2020

2 questions on this:
Q1. Can you also use 3rd part library as a drag target. Use case would be using reactstrap "Container" as a target and dropping "Row" or other components inside it to build a page
Q2. Looks like findDOMNode does not work on functional components. How to do have drag/drop with some 3rd party components if they are functional?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants