【问题标题】:React DnD: Avoid using findDOMNodeReact DnD:避免使用 findDOMNode
【发布时间】:2017-03-22 19:24:30
【问题描述】:

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

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

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

Source

编辑

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

【问题讨论】:

    标签: javascript reactjs react-dom react-dnd strict-mode


    【解决方案1】:

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

    当在 HTML 元素上使用 ref 属性时,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/

    【讨论】:

    • 我让它在this example 中工作,但我仍然无法在我的实时代码中工作。我想知道是不是因为我的元素既是放置目标又是放置源。我会再做一些测试。
    • 是的,它似乎无法在 this example(见第 25 行)中工作,其中我的组件是 DnD 目标和源。
    • @joshhunt 查看更新。我认为它现在正在做你所期望的
    • 感谢您的解释。 !
    • 不幸的是,回调 ref 的凝聚力较低,因此比使用 findDOMNode 抓取元素的可读性差。声明嵌套在标记中的新属性作为一般模式是有问题的。
    【解决方案2】:

    更好的解决方案

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

    <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 &amp;&amp; 是为了避免在未定义的对象上调用getBoundingClientRect)

    findDOMNode 的替代品

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

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

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

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

    其他(较弱)替代方案

    使用monitor.getDifferenceFromInitialOffset();,它不会给你精确的值,但如果你有一个小的dragSource,可能就足够了。然后返回值是相当可预测的,误差范围很小,具体取决于 dragSource 的大小。

    【讨论】:

      【解决方案3】:

      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 = node ref 方法,您都依赖于底层的 React 节点。我更喜欢这个,因为消费者不必记住手动使用 ref 而不是 React-DnD 已经要求的,而且消费者也不必是类组件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-09
        • 1970-01-01
        • 2022-10-18
        • 1970-01-01
        • 2018-11-14
        • 2020-01-01
        • 2017-05-11
        相关资源
        最近更新 更多