React-dnd: "canDrop", как узнать, над каким элементом мы находимся?

Созданный на 14 апр. 2016  ·  4Комментарии  ·  Источник: react-dnd/react-dnd

В методе drop(props, monitor, component) у нас есть три аргумента. Но в canDrop(props, monitor) нет component arg.

Я хочу, чтобы при перетаскивании элемента на другой компонент я «разбивал» этот компонент на две части по 50% (высота). Если предмет упал в верхнюю часть, он будет помещен над ним, если он упадет в нижнюю часть, он будет помещен после.

У меня уже есть код, который определяет, упал ли компонент в верхнюю / нижнюю часть. Сейчас я хочу добавить класс в зависимости от того, находится ли мышь в верхней / нижней части (но во время действия наведения, а не действия перетаскивания).

Я не знаю, могу ли я использовать метод hover(props, monitor, component) , потому что я не понимаю, как я должен добавить свойство в свой компонент (который добавил бы класс CSS в DOM или что-то подобное) из это, поскольку он не принадлежит самому компоненту и не имеет доступа к его контексту и так далее.

Думаю, я что-то упускаю. :)

Самый полезный комментарий

Итак, идея состоит в том, что вы хотите установить состояние в своем компоненте, а затем, когда состояние установлено, вы добавляете класс.

Допустим, вы перетаскиваете Foo в панель:

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 выше, заключается в том, что response-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 в панель:

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 выше, заключается в том, что response-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.

    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 рейтинги