React-dnd: "canDrop", ¿cómo saber sobre qué elemento estamos encima?

Creado en 14 abr. 2016  ·  4Comentarios  ·  Fuente: react-dnd/react-dnd

En el método drop(props, monitor, component) , tenemos tres argumentos. Pero en el canDrop(props, monitor) no existe el component arg.

Quiero que cuando un elemento se arrastra sobre otro componente, "divido" ese componente en dos partes del 50% (altura). Si el artículo se deja caer en la parte superior, se colocará encima de él, si se deja caer en la parte inferior, se colocará después.

Ya tengo el código que detecta si el componente se deja caer en la parte superior / inferior. Lo que quiero ahora es agregar una clase en función de si el mouse está en la parte superior / inferior (pero durante la acción de desplazamiento, no la acción de soltar).

No sé si puedo usar el método hover(props, monitor, component) , porque no veo cómo se supone que debo agregar una propiedad en mi componente (que agregaría una clase CSS en el DOM o similar) desde ya que no pertenece al componente en sí y no tiene acceso a su contexto y así sucesivamente.

Supongo que me estoy perdiendo algo. :)

Comentario más útil

Bien, entonces la idea es que desea establecer el estado en su componente, y luego, cuando se establece el estado, agrega la clase.

Digamos que estás arrastrando un Foo a un 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",
    });
  }
}

¡La única pregunta es cómo establecemos ese estado!

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");
    }
  },
  ...
}

Básicamente, desea llamar a un método de instancia de su componente, setFooHover , que llamará setState . Eso debería verse así por razones de rendimiento:

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

La razón por la que llamo undecorate arriba es que react-dnd usa componentes de orden superior y queremos los componentes sin empaquetar. En mi opinión, esta es una complicación desagradable de componentes de orden superior. Tal vez podrías implementar esto enviando una acción a tu tienda de flujo o algo así, pero en caso de que decidas hacerlo como yo, aquí está mi undecorate :

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

  return curr;
}

Ahora bien, si implementa esto, todo se verá bien excepto por un pequeño problema. ¡La clase nunca se va! Nadie llama a setHoverState(null) (no existe una función endHover en su barTarget). La forma correcta de manejar esto es usar componentWillReceiveProps así:

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

(No olvide agregar { isFooOver: monitor.isOver() } en su función de recolección)

¡Déjame saber como va!

Todos 4 comentarios

¡Oye, estás de suerte! He implementado casi exactamente esto antes, así que permítanme armar un poco de código.

Bien, entonces la idea es que desea establecer el estado en su componente, y luego, cuando se establece el estado, agrega la clase.

Digamos que estás arrastrando un Foo a un 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",
    });
  }
}

¡La única pregunta es cómo establecemos ese estado!

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");
    }
  },
  ...
}

Básicamente, desea llamar a un método de instancia de su componente, setFooHover , que llamará setState . Eso debería verse así por razones de rendimiento:

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

La razón por la que llamo undecorate arriba es que react-dnd usa componentes de orden superior y queremos los componentes sin empaquetar. En mi opinión, esta es una complicación desagradable de componentes de orden superior. Tal vez podrías implementar esto enviando una acción a tu tienda de flujo o algo así, pero en caso de que decidas hacerlo como yo, aquí está mi undecorate :

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

  return curr;
}

Ahora bien, si implementa esto, todo se verá bien excepto por un pequeño problema. ¡La clase nunca se va! Nadie llama a setHoverState(null) (no existe una función endHover en su barTarget). La forma correcta de manejar esto es usar componentWillReceiveProps así:

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

(No olvide agregar { isFooOver: monitor.isOver() } en su función de recolección)

¡Déjame saber como va!

¡Muchas gracias por la explicación detallada! Yo lo haré saber :)

Finalmente, utilicé el enfoque de Redux, así que básicamente envié un evento a través de un reductor cuando se disparó el evento de caída.

    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}))

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

BrennanRoberts picture BrennanRoberts  ·  3Comentarios

bebbi picture bebbi  ·  3Comentarios

redochka picture redochka  ·  3Comentarios

Okami92 picture Okami92  ·  3Comentarios

gaearon picture gaearon  ·  4Comentarios