React-dnd: "canDrop", woher wissen wir, auf welchem ​​Element wir oben sind?

Erstellt am 14. Apr. 2016  ·  4Kommentare  ·  Quelle: react-dnd/react-dnd

In der Methode drop(props, monitor, component) haben wir drei Argumente. Aber im canDrop(props, monitor) gibt es nicht das component Argument.

Ich möchte, dass, wenn ein Element über eine andere Komponente gezogen wird, ich diese Komponente in zwei Teile von 50% (Höhe) "aufspalte". Wenn der Gegenstand im oberen Teil abgelegt wird, wird er darüber platziert, wenn er im unteren Teil abgelegt wird, wird er danach platziert.

Ich habe bereits den Code, der erkennt, ob die Komponente im oberen / unteren Teil abgelegt wird. Was ich jetzt möchte, ist, eine Klasse hinzuzufügen, die darauf basiert, ob sich die Maus im oberen / unteren Teil befindet (aber während der Hover-Aktion, nicht der Drop-Aktion).

Ich weiß nicht, ob ich die Methode hover(props, monitor, component) verwenden kann, weil ich nicht sehe, wie ich eine Eigenschaft in meiner Komponente hinzufügen soll (die eine CSS-Klasse im DOM oder ähnliches hinzufügen würde) von es, da es nicht zur Komponente selbst gehört und keinen Zugriff auf ihren Kontext hat und so weiter.

Ich glaube, mir fehlt etwas. :)

Hilfreichster Kommentar

OK, die Idee ist also, dass Sie den Status in Ihrer Komponente festlegen möchten, und wenn der Status festgelegt ist, fügen Sie die Klasse hinzu.

Angenommen, Sie ziehen einen Foo in eine 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",
    });
  }
}

Die Frage ist nur, wie wir diesen Zustand herstellen!

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

Im Grunde möchten Sie also eine Instanzmethode Ihrer Komponente aufrufen, setFooHover , die setState . Das sollte aus Performancegründen so aussehen:

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

Der Grund, warum ich oben undecorate rufe, ist, dass react-dnd Komponenten höherer Ordnung verwendet, und wir wollen die unverpackten Komponenten. Dies ist meiner Meinung nach eine hässliche Komplikation von Komponenten höherer Ordnung. Vielleicht könnten Sie dies stattdessen implementieren, indem Sie eine Aktion an Ihren Flux-Shop senden oder so, aber falls Sie sich entscheiden, es wie ich zu tun, hier ist mein undecorate :

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

  return curr;
}

Wenn Sie dies jetzt implementieren, wird alles gut aussehen, bis auf ein kleines Problem. Die Klasse geht nie weg! Niemand ruft jemals setHoverState(null) (es gibt keine endHover-Funktion in Ihrem barTarget). Der richtige Weg, dies zu handhaben, besteht darin, componentWillReceiveProps wie folgt zu verwenden:

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

(Vergiss nicht { isFooOver: monitor.isOver() } in deiner Sammelfunktion hinzuzufügen)

Lass mich wissen wie es geht!

Alle 4 Kommentare

Hey, du hast Glück! Ich habe fast genau dies schon einmal implementiert, also lassen Sie mich etwas Code zusammenstellen.

OK, die Idee ist also, dass Sie den Status in Ihrer Komponente festlegen möchten, und wenn der Status festgelegt ist, fügen Sie die Klasse hinzu.

Angenommen, Sie ziehen einen Foo in eine 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",
    });
  }
}

Die Frage ist nur, wie wir diesen Zustand herstellen!

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

Im Grunde möchten Sie also eine Instanzmethode Ihrer Komponente aufrufen, setFooHover , die setState . Das sollte aus Performancegründen so aussehen:

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

Der Grund, warum ich oben undecorate rufe, ist, dass react-dnd Komponenten höherer Ordnung verwendet, und wir wollen die unverpackten Komponenten. Dies ist meiner Meinung nach eine hässliche Komplikation von Komponenten höherer Ordnung. Vielleicht könnten Sie dies stattdessen implementieren, indem Sie eine Aktion an Ihren Flux-Shop senden oder so, aber falls Sie sich entscheiden, es wie ich zu tun, hier ist mein undecorate :

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

  return curr;
}

Wenn Sie dies jetzt implementieren, wird alles gut aussehen, bis auf ein kleines Problem. Die Klasse geht nie weg! Niemand ruft jemals setHoverState(null) (es gibt keine endHover-Funktion in Ihrem barTarget). Der richtige Weg, dies zu handhaben, besteht darin, componentWillReceiveProps wie folgt zu verwenden:

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

(Vergiss nicht { isFooOver: monitor.isOver() } in deiner Sammelfunktion hinzuzufügen)

Lass mich wissen wie es geht!

Vielen Dank für die ausführliche Erklärung! Ich lasse es dich wissen :)

Schließlich habe ich den Redux-Ansatz verwendet, also habe ich im Grunde ein Ereignis durch einen Reducer gesendet, wenn das Drop-Ereignis ausgelöst wurde.

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

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen