React-dnd: How to use a third-party component as a drag source?

Created on 18 Dec 2015  ·  9Comments  ·  Source: react-dnd/react-dnd

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?

Most helpful comment

Now we have this issue for googling too :-)

Fortunately! :grin:

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);
}}

All 9 comments

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).

(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.)

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(…)

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

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!

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.

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.

Now we have this issue for googling too :-)

Fortunately! :grin:

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);
}}

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?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

FutureProg picture FutureProg  ·  3Comments

rubayethossain picture rubayethossain  ·  3Comments

bebbi picture bebbi  ·  3Comments

gocreating picture gocreating  ·  4Comments

redochka picture redochka  ·  3Comments