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

Examples using findDOMNode() #591

Closed
sunjay opened this issue Dec 6, 2016 · 11 comments
Closed

Examples using findDOMNode() #591

sunjay opened this issue Dec 6, 2016 · 11 comments
Labels

Comments

@sunjay
Copy link

sunjay commented Dec 6, 2016

Using findDOMNode() is no longer recommended. In fact, if you use it, React will warn you not to. Some of the examples use findDOMNode in places where I don't think you can use refs to replace them. These should be updated to use something else if we shouldn't be using findDOMNode anymore.

One specific example of this is here: https://github.com/gaearon/react-dnd/blob/master/examples/04%20Sortable/Simple/Card.js#L34

const cardTarget = {
  hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.moveCard(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  }
};

Other examples: https://github.com/gaearon/react-dnd/search?l=JavaScript&q=finddomnode&utf8=%E2%9C%93

What is the recommended way to resolve this?

@beniutek
Copy link

beniutek commented Jan 21, 2017

recommended way of doing it is by using refs, see: https://facebook.github.io/react/docs/refs-and-the-dom.html

you can then access passed refs by calling component.passedRefName.getBoundingClientRect()

@briandeheus
Copy link

The way we've fixed it on our end is as follows:

In our draggable:

<li className="draggable-card" ref={(elm) => {this.card = elm;}}>

And inside of our DropTarget hover method.

hover (props, monitor, component) {
     // code
    const {decoratedComponentInstance} = component;
    const hoverBoundingRect = decoratedComponentInstance.card.getBoundingClientRect();
    // code
}

I'm not sure if there are better ways to go about this, maybe @gaearon can shine some light on this. That would be greatly appreciated.

@beniutek
Copy link

That's the recommended way from facebook docs. We are doing it in the same fashion

@darthtrevino
Copy link
Member

If I have some time I'll look at this later. In the meantime feel free to cut a PR

@ndelage
Copy link

ndelage commented Feb 21, 2017

We're taking the ref approach, but component.decoratedComponentInstance is null.

Just for reference, our components are both draggable & droppable:

export default flow(
  DragSource('NavItem', navItemDragSource, dragCollect),
  DropTarget('NavItem', navItemDropSource, dropCollect)
)(NavigationItem);

Our NavigationItem component sets the ref we expect to use in hover:

class NavigationItem extends React.Component
  render() {
    return this.props.connectDropTarget(this.props.connectDragSource(
      <section
        ref={element => (this.navItemElement = element)}
     </section>
  }
}

And in hover:

const navItemDropSource = {
  hover: (props, monitor, component) => {
    console.log(component.decoratedComponentInstance); // prints null
  }
}

Any idea what we're missing here?

@beniutek
Copy link

beniutek commented Feb 21, 2017

@ndelage

In our app it looks roughly like that:

..some code

const itemTarget = {
  hover(props, monitor, component) {
    if (monitor.isOver()) {
      ...some code

       const bounds = component.node.getBoundingClientRect()

       ...some code
       return result;
    }
  }
}

..some code

@DragSource(ITEM_TYPE, itemSource, collectSource)
@DropTarget(ITEM_TYPE, itemTarget, collectDrop)

class DraggableItem extends Component {
  render() {
    const { connectDragSource, connectDropTarget } = this.props;

    return connectDragSource(
      connectDropTarget(
        <div
          className="draggable-item"
          ref={ node => (this.node = node) }
        >
          <Item { ...this.props } />
        </div>
      )
    );
  }
}

export default DraggableItem

in your example I see that you are assigning the element param to this.naItemElement but in hover method you are not referring to naItemElement at all. You should call it something like that:

const navItemDropSource = {
  hover: (props, monitor, component) => {
    console.log(component.navItemElement); // you should get the DOM node reference now
  }
}

@ndelage
Copy link

ndelage commented Feb 21, 2017

Got is working! It turns out the trick was to call connectDropTarget first. I don't think connectDragSource sets decoratedComponentInstance (for whatever reason the ref variable isn't set on component, it's only available on component.decoratedComponentInstance).

Our working version looks like this:

class NavigationItem extends React.Component
  render() {
    return this.props.connectDragSource(this.props.connectDropTarget(
      <section
        ref={node => (this.node = node)}
      >
       {this.props.name}
      </section>
    ));
  }
}
const navItemDropSource = {
  hover: (props, monitor, component) => {
    console.log(component.decoratedComponentInstance.node.getBoundingClientRect());
  }
}

Thanks for the help @beniutek

@mkaisercross
Copy link

mkaisercross commented Aug 15, 2017

I am glad I am not the only one worried about this feature being deprecated. I am currently using a 3rd party 'Webcam' component found here https://github.com/mozmorris/react-webcam. This component uses findDOMNode and I do not see how refs can be used to replace the functionality. Note: I do see that refs are being used here but if you look in the (very short) source code in src/react-webcam.js, it is also using findDOMNode.

Here is the simple example of how you can use this component.

class WebcamCapture extends React.Component {
  setRef = (webcam) => {
    this.webcam = webcam;
  }

  capture = () => {
    const imageSrc = this.webcam.getScreenshot();
  };

  render() {
    return (
      <div>
        <Webcam
          audio={false}
          height={350}
          ref={this.setRef}
          screenshotFormat="image/jpeg"
          width={350}
        />
        <button onClick={this.capture}>Capture photo</button>
      </div>
    );
  }
}

I think if you wanted to change this to use refs thenthe example code would require the user of this 3rd party library to understand the dom structure of the Webcam component which is using a video dom element and the getusermedia API. Requiring a 3rd party component user to understand the DOM structure of the component is not ideal to say the least. Please let me know if I am missing a simpler way to do this using refs.

@QuentinRoy
Copy link

QuentinRoy commented Jan 8, 2019

For anyone who struggles with this issue and tries to use the solution of @ndelage and @beniutek, component.decoratedComponentInstance has been replaced by component.getDecoratedComponentInstance() (documented here).

Here is what you need:

class NavigationItem extends React.Component
  constructor(props){
    super(props);
    this.node = React.createRef();
  }
  render() {
    const { connectDragSource, connectDropTarget } = this.props;
    return connectDragSource(connectDropTarget(
      <section ref={this.node}>
       {this.props.name}
      </section>
    ));
  }
}
const navItemDropSource = {
  hover: (props, monitor, component) => {
    console.log(component.getDecoratedComponentInstance().node.getBoundingClientRect());
  }
}

I hope someone will soon be able to update the example.

@valse
Copy link

valse commented Apr 17, 2019

I need to add some pins over an image and to get the drop component's viewport positions, I added the ref node like @QuentinRoy suggested but the getDecoratedComponentInstance() didn't work for me (is not a function).

I saw that I can get the node reference directly on the component param in the drop method of the drop target spec:

drop: (props, monitor, component) => {
   console.log(component.node.current.getBoundingClientRect())
}

@stale
Copy link

stale bot commented Jul 6, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jul 6, 2019
@stale stale bot closed this as completed Jul 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants