React-dnd: "canDrop", bagaimana cara mengetahui elemen kita berada di atas?

Dibuat pada 14 Apr 2016  ·  4Komentar  ·  Sumber: react-dnd/react-dnd

Dalam metode drop(props, monitor, component) , kita memiliki tiga argumen. Tetapi di canDrop(props, monitor) tidak ada argumen component .

Saya ingin ketika suatu item diseret ke komponen lain, saya "membagi" komponen itu menjadi dua bagian 50% (tinggi). Jika item dijatuhkan di bagian yang lebih tinggi maka dia akan ditempatkan di atasnya, jika dijatuhkan di bagian bawah maka akan ditempatkan setelahnya.

Saya sudah memiliki kode yang mendeteksi apakah komponen dijatuhkan di bagian atas/bawah. Yang saya inginkan sekarang adalah menambahkan kelas berdasarkan apakah mouse ada di bagian atas/bawah (tetapi selama tindakan melayang, bukan tindakan jatuhkan).

Saya tidak tahu apakah saya dapat menggunakan metode hover(props, monitor, component) , karena saya tidak melihat bagaimana saya seharusnya menambahkan properti di komponen saya (yang akan menambahkan kelas CSS di DOM atau serupa) dari itu, karena itu bukan milik komponen itu sendiri dan tidak memiliki akses ke konteksnya dan seterusnya.

Kurasa aku kehilangan sesuatu. :)

Komentar yang paling membantu

Oke, jadi idenya adalah Anda ingin menyetel status di komponen Anda, lalu saat status disetel, Anda menambahkan kelas.

Katakanlah Anda menyeret Foo ke dalam 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",
    });
  }
}

Satu-satunya pertanyaan adalah bagaimana kita mengatur status itu!

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

Jadi pada dasarnya, Anda ingin memanggil metode instan dari komponen Anda, setFooHover , yang akan memanggil setState . Itu akan terlihat seperti ini karena alasan kinerja:

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

Alasan mengapa saya memanggil undecorate atas adalah karena react-dnd menggunakan komponen tingkat tinggi, dan kami menginginkan komponen yang tidak dibungkus. Ini adalah komplikasi jelek dari komponen tingkat tinggi menurut saya. Mungkin Anda bisa menerapkan ini sebagai gantinya dengan mengirimkan tindakan ke toko flux Anda atau sesuatu, tetapi jika Anda memutuskan untuk melakukannya seperti saya, inilah undecorate :

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

  return curr;
}

Sekarang jika Anda menerapkan ini, semuanya akan terlihat bagus kecuali untuk satu masalah kecil. Kelas tidak pernah pergi! Tidak ada yang pernah memanggil setHoverState(null) (tidak ada fungsi endHover di barTarget Anda). Cara yang tepat untuk menangani ini adalah dengan menggunakan componentWillReceiveProps seperti ini:

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

(Jangan lupa untuk menambahkan { isFooOver: monitor.isOver() } dalam fungsi pengumpulan Anda)

Beri tahu saya bagaimana kelanjutannya!

Semua 4 komentar

Hei, Anda beruntung! Saya telah menerapkan hampir persis ini sebelumnya, jadi izinkan saya mengumpulkan beberapa kode.

Oke, jadi idenya adalah Anda ingin menyetel status di komponen Anda, lalu saat status disetel, Anda menambahkan kelas.

Katakanlah Anda menyeret Foo ke dalam 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",
    });
  }
}

Satu-satunya pertanyaan adalah bagaimana kita mengatur status itu!

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

Jadi pada dasarnya, Anda ingin memanggil metode instan dari komponen Anda, setFooHover , yang akan memanggil setState . Itu akan terlihat seperti ini karena alasan kinerja:

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

Alasan mengapa saya memanggil undecorate atas adalah karena react-dnd menggunakan komponen tingkat tinggi, dan kami menginginkan komponen yang tidak dibungkus. Ini adalah komplikasi jelek dari komponen tingkat tinggi menurut saya. Mungkin Anda bisa menerapkan ini sebagai gantinya dengan mengirimkan tindakan ke toko flux Anda atau sesuatu, tetapi jika Anda memutuskan untuk melakukannya seperti saya, inilah undecorate :

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

  return curr;
}

Sekarang jika Anda menerapkan ini, semuanya akan terlihat bagus kecuali untuk satu masalah kecil. Kelas tidak pernah pergi! Tidak ada yang pernah memanggil setHoverState(null) (tidak ada fungsi endHover di barTarget Anda). Cara yang tepat untuk menangani ini adalah dengan menggunakan componentWillReceiveProps seperti ini:

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

(Jangan lupa untuk menambahkan { isFooOver: monitor.isOver() } dalam fungsi pengumpulan Anda)

Beri tahu saya bagaimana kelanjutannya!

Terima kasih banyak atas penjelasan detailnya! Aku akan memberitahu Anda :)

Akhirnya, saya menggunakan pendekatan Redux, jadi pada dasarnya saya mengirimkan sebuah acara melalui peredam ketika acara drop dipecat.

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

Apakah halaman ini membantu?
0 / 5 - 0 peringkat