React DnD:避免使用 findDOMNode

IT技术 javascript reactjs react-dom react-dnd strict-mode
2021-04-23 04:56:45

我不完全理解它,但显然不建议使用 findDOMNode()

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

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

来源

编辑

这可能是因为我的组件既是拖放源又是目标,因为我可以让它在这个例子中工作,但不是这个

3个回答

假设您使用的是 es6 类语法和最新版本的 React(在撰写本文时为 15),您可以附加一个回调引用,就像 Dan 在您共享的链接上的示例中所做的那样。文档

当 ref 属性用于 HTML 元素时,ref 回调接收底层 DOM 元素作为其参数。例如,此代码使用 ref 回调来存储对 DOM 节点的引用:

<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/

我在这个例子中让它工作,但我仍然无法在我的实时代码中工作。我想知道是不是因为我的元素既是放置目标又是放置源。我会再做一些测试。
2021-05-28 04:56:45
不幸的是,回调引用的内聚性较差,因此与使用 findDOMNode 抓取元素相比可读性较差。声明嵌套在标记中的新属性作为一般模式是有问题的。
2021-06-10 04:56:45
是的,在我的组件是 DnD 目标和源的这个例子(见第 25 行)中似乎无法让它工作
2021-06-11 04:56:45
@joshhunt 查看更新。我认为它现在正在做你期望的
2021-06-11 04:56:45
感谢您的解释。
2021-06-20 04:56:45

更好的解决方案

更好的解决方案是用 a 包裹可拖动组件,在其上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

如果您不想添加 wrapping div,您可以执行以下操作。@imjared 的回复和这里建议的解决方案不起作用(至少在react-dnd@2.3.0和 中react@15.3.1)。

唯一findDOMNode(component).getBoundingClientRect();不使用的工作替代方案findDOMNode是:

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

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

其他(较弱的)替代品

使用monitor.getDifferenceFromInitialOffset();它不会给你精确的值,但如果你有一个小的dragSource可能已经足够了然后根据您的dragSource的大小,返回的值是非常可预测的,误差很小

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

例如,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.

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