React DnD:避免使用 findDOMNode

React DnD: Avoid using findDOMNode

我不完全理解,但显然 it isn't recommended to use findDOMNode()

我正在尝试创建拖放组件,但我不确定应该如何从组件变量访问引用。这是我目前拥有的示例:

const cardTarget = {
    hover(props, monitor, component) {
        ...
        // Determine rectangle on screen
        const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
        ...
    }
}

Source

编辑

这可能是因为我的组件既是拖放源又是目标,因为我可以让它在 this example but not this one 中工作。

假设您使用的是 es6 class 语法和最新版本的 React(撰写本文时为 15),您可以像 Dan 在 link 你分享了。来自 the docs:

When the ref attribute is used on an HTML element, the ref callback receives the underlying DOM element as its argument. For example, this code uses the ref callback to store a reference to a DOM node:

<h3
    className="widget"
    onMouseOver={ this.handleHover.bind( this ) }
    ref={node => this.node = node}
>

然后就可以像我们以前和老朋友一样访问节点了findDOMNode()或者getDOMNode():

handleHover() {
  const rect = this.node.getBoundingClientRect(); // Your DOM node
  this.setState({ rect });
}

在行动中: https://jsfiddle.net/ftub8ro6/

编辑:

因为 React DND 在幕后做了一些魔术,我们必须使用他们的 API 来获取装饰组件。他们提供 getDecoratedComponentInstance() 以便您可以访问底层组件。使用它后,您可以按预期获得 component.node

hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;
    const rawComponent = component.getDecoratedComponentInstance();
    console.log( rawComponent.node.getBoundingClientRect() );
    ...

这是实际操作:

https://jsfiddle.net/h4w4btz9/2/

更好的解决方案

更好的解决方案是用 div 包裹可拖动组件,在其上定义一个 ref 并将其传递给可拖动组件,即

<div key={key} ref={node => { this.node = node; }}>
  <MyComponent
    node={this.node}
  />
</div>

MyComponent 包裹在 DragSource 中。现在你可以使用

hover(props, monitor, component) {
  ...
  props.node && props.node.getBoundingClientRect();
  ...
}

(添加props.node &&是为了避免在未定义的对象上调用getBoundingClientRect

替代findDOMNode

如果您不想添加包装 div,您可以执行以下操作。 @imjared 和 的回复不起作用(至少在 react-dnd@2.3.0react@15.3.1 中)。

不使用 findDOMNodefindDOMNode(component).getBoundingClientRect(); 唯一可行的替代方法是:

hover(props, monitor, component) {
  ...
  component.decoratedComponentInstance._reactInternalInstance._renderedComponent._hostNode.getBoundingClientRect();
  ...
}

这不是很漂亮而且危险因为react可能会在未来的版本中改变这个内部路径!

其他(较弱)备选方案

使用 monitor.getDifferenceFromInitialOffset();,它不会给你精确的值,但在你有一个小的 dragSource 的情况下可能已经足够了。然后返回的值是非常可预测的,误差很小,具体取决于 dragSource.

的大小

React-DnD 的 API 非常灵活——我们可以(滥用)使用它。

例如,React-DnD 让我们确定将哪些连接器传递给底层组件。这意味着我们也可以包装它们。 :)

例如,让我们覆盖目标连接器以将节点存储在监视器上。我们将使用 Symbol 这样我们就不会将这个小黑客泄露给外界。

const NODE = Symbol('Node')

function targetCollector(connect, monitor) {
  const connectDropTarget = connect.dropTarget()
  return {
    // Consumer does not have to know what we're doing ;)
    connectDropTarget: node => {
      monitor[NODE] = node
      connectDropTarget(node)
    }
  }
}

现在在您的 hover 方法中,您可以使用

const node = monitor[NODE]
const hoverBoundingRect = node.getBoundingClientRect()

这种方法搭载了 React-DnD 的流程,并通过使用 Symbol.

屏蔽了外界

无论您使用的是这种方法还是基于 class 的 this.node = node ref 方法,您都依赖于底层的 React 节点。我更喜欢这个,因为消费者不必记住手动使用 ref 除了 React-DnD 已经需要的那些,而且消费者也不必是 class 组件。