React-dnd: “canDrop”,如何知道我们在什么元素之上?

创建于 2016-04-14  ·  4评论  ·  资料来源: react-dnd/react-dnd

drop(props, monitor, component)方法中,我们有三个参数。 但是在canDrop(props, monitor)中没有component参数。

我希望当一个项目被拖到另一个组件上时,我将该组件“拆分”为 50%(高度)的两部分。 如果物品掉落在较高的部分,那么他将被放置在它的上方,如果它掉落在较低的部分,那么它将被放置在其后。

我已经有了检测组件是否掉在顶部/底部的代码。 我现在想要的是根据鼠标是否在顶部/底部添加一个类(但在悬停动作期间,而不是放置动作期间)。

我不知道我是否可以使用hover(props, monitor, component)方法,因为我不知道我应该如何在我的组件中添加一个属性(这将在 DOM 中添加一个 CSS 类或类似的)来自它,因为它不属于组件本身并且无权访问其上下文等等。

我想我错过了一些东西。 :)

最有用的评论

好的,所以这个想法是你想在你的组件中设置状态,然后当状态设置好时,你添加类。

假设您将 Foo 拖入 Bar:

import React, { Component } from "react";
import classNames from "classnames";

class Bar extends Component {
  constructor(props) {
    super(props);

    this.state = {
      // Where a foo is hovering over the component.  One of null, "above", or "below"
      fooHover: null,
    };
  }

  render() {
    const { fooHover } = this.state;

    const classes = classNames("bar", {
      "bar-above": fooHover === "above",
      "bar-below": fooHover === "below",
    });
  }
}

唯一的问题是我们如何设置该状态!

import { findDOMNode } from "react-dom";

const barTarget = {
  ...
  hover(props, monitor, component) {
    if (!monitor.canDrop()) {
      return;
    }

    const rawComponent = undecorate(component); // undecorate described below

    const { y } = monitor.getClientOffset();
    const { top, height } = findDOMNode(component).getBoundingClientRect();

    if (y < top + height/2) {
      rawComponent.setFooHover("above"); // setFooHover described below
    } else {
      rawComponent.setFooHover("below");
    }
  },
  ...
}

所以基本上,您想调用组件的实例方法setFooHover ,它将调用setState 。 出于性能原因,它应该如下所示:

  // In Bar
  setFooHover(fooHover) {
    if (fooHover !== this.state.fooHover) {
      this.setState({ fooHover });
    }
  }

我之所以在上面调用undecorate是因为 react-dnd 使用了高阶组件,我们想要未包装的组件。 在我看来,这是高阶组件的丑陋复杂化。 也许您可以通过将操作分派到您的通量存储或其他东西来实现这一点,但如果您决定像我一样这样做,这是我的undecorate

function undecorate(component) {
  let curr = component;
  while (typeof curr.getDecoratedComponentInstance === "function") {
    curr = curr.getDecoratedComponentInstance();
  }

  return curr;
}

现在,如果你实现了这一点,除了一个小问题外,一切都会看起来很好。 课堂永远不会消失! 从来没有人调用过setHoverState(null) (barTarget 中没有像 endHover 函数这样的东西)。 处理这个问题的正确方法是像这样使用componentWillReceiveProps

  componentWillReceiveProps(nextProps) {
    if (this.props.isFooOver && !nextProps.isFooOver) {
      this.setState({ fooHover: null });
    }
  }

(不要忘记在你的收藏函数中添加{ isFooOver: monitor.isOver() }

让我知道事情的后续!

所有4条评论

嘿,你很幸运! 我以前几乎完全实现过这个,所以让我把一些代码放在一起。

好的,所以这个想法是你想在你的组件中设置状态,然后当状态设置好时,你添加类。

假设您将 Foo 拖入 Bar:

import React, { Component } from "react";
import classNames from "classnames";

class Bar extends Component {
  constructor(props) {
    super(props);

    this.state = {
      // Where a foo is hovering over the component.  One of null, "above", or "below"
      fooHover: null,
    };
  }

  render() {
    const { fooHover } = this.state;

    const classes = classNames("bar", {
      "bar-above": fooHover === "above",
      "bar-below": fooHover === "below",
    });
  }
}

唯一的问题是我们如何设置该状态!

import { findDOMNode } from "react-dom";

const barTarget = {
  ...
  hover(props, monitor, component) {
    if (!monitor.canDrop()) {
      return;
    }

    const rawComponent = undecorate(component); // undecorate described below

    const { y } = monitor.getClientOffset();
    const { top, height } = findDOMNode(component).getBoundingClientRect();

    if (y < top + height/2) {
      rawComponent.setFooHover("above"); // setFooHover described below
    } else {
      rawComponent.setFooHover("below");
    }
  },
  ...
}

所以基本上,您想调用组件的实例方法setFooHover ,它将调用setState 。 出于性能原因,它应该如下所示:

  // In Bar
  setFooHover(fooHover) {
    if (fooHover !== this.state.fooHover) {
      this.setState({ fooHover });
    }
  }

我之所以在上面调用undecorate是因为 react-dnd 使用了高阶组件,我们想要未包装的组件。 在我看来,这是高阶组件的丑陋复杂化。 也许您可以通过将操作分派到您的通量存储或其他东西来实现这一点,但如果您决定像我一样这样做,这是我的undecorate

function undecorate(component) {
  let curr = component;
  while (typeof curr.getDecoratedComponentInstance === "function") {
    curr = curr.getDecoratedComponentInstance();
  }

  return curr;
}

现在,如果你实现了这一点,除了一个小问题外,一切都会看起来很好。 课堂永远不会消失! 从来没有人调用过setHoverState(null) (barTarget 中没有像 endHover 函数这样的东西)。 处理这个问题的正确方法是像这样使用componentWillReceiveProps

  componentWillReceiveProps(nextProps) {
    if (this.props.isFooOver && !nextProps.isFooOver) {
      this.setState({ fooHover: null });
    }
  }

(不要忘记在你的收藏函数中添加{ isFooOver: monitor.isOver() }

让我知道事情的后续!

非常感谢详细的解释! 我会告诉你 :)

最后,我使用了 Redux 的方法,所以我基本上是在 drop 事件被触发时通过 reducer 调度一个事件。

    drop: (propsTargetItem, monitor, targetItem) ->
        draggedItem = monitor.getItem()# Item that has been dragged and eventually dropped.
        coordsDrop = monitor.getClientOffset()# Coords of the drop action.
        diff = monitor.getDifferenceFromInitialOffset()# Comparison of the initial click (dragging start) against the end of the click (dropping start).

        patternId = draggedItem.patternId
        patternPosition = draggedItem.position
        targetId = propsTargetItem.context.patternId
        targetPosition = propsTargetItem.context.position
        isCursorAboveHalf = GeoService.isAboveHalf(ReactDOM.findDOMNode(targetItem), coordsDrop.y)

        # Extract positions in the array of children.
        oldPosition = draggedItem.position# Current position of the item, which will be the old position as soon as the item is moved. (by the server through sockets)
        newPosition = calcDropPosition(targetId, patternId, patternPosition, targetPosition, isCursorAboveHalf)# Position where we will move the dragged item.

        # Dispatch only if the position has changed and is set.
        if oldPosition isnt newPosition and newPosition?
            draggedItem.moveDnDPattern(Object.assign({}, draggedItem, {newPosition: newPosition, oldPosition: oldPosition}))

此页面是否有帮助?
0 / 5 - 0 等级