React-dnd: "canDrop" ، كيف نعرف ما هو العنصر الذي نحن على رأسه؟

تم إنشاؤها على ١٤ أبريل ٢٠١٦  ·  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 أعلاه هو أن رد فعل dnd يستخدم مكونات ذات ترتيب أعلى ، ونريد المكونات غير المغلفة. هذا هو تعقيد قبيح للمكونات ذات الترتيب الأعلى في رأيي. ربما يمكنك تنفيذ هذا بدلاً من ذلك عن طريق إرسال إجراء إلى متجر التدفق الخاص بك أو شيء من هذا القبيل ، ولكن في حال قررت القيام بذلك مثلي ، فإليك undecorate :

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

  return curr;
}

الآن إذا قمت بتنفيذ هذا ، فسيبدو كل شيء على ما يرام باستثناء مشكلة صغيرة واحدة. الفصل لا يذهب بعيدا! لم يتصل أحد أبدًا بـ setHoverState(null) (لا يوجد شيء مثل وظيفة 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 أعلاه هو أن رد فعل dnd يستخدم مكونات ذات ترتيب أعلى ، ونريد المكونات غير المغلفة. هذا هو تعقيد قبيح للمكونات ذات الترتيب الأعلى في رأيي. ربما يمكنك تنفيذ هذا بدلاً من ذلك عن طريق إرسال إجراء إلى متجر التدفق الخاص بك أو شيء من هذا القبيل ، ولكن في حال قررت القيام بذلك مثلي ، فإليك undecorate :

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

  return curr;
}

الآن إذا قمت بتنفيذ هذا ، فسيبدو كل شيء على ما يرام باستثناء مشكلة صغيرة واحدة. الفصل لا يذهب بعيدا! لم يتصل أحد أبدًا بـ setHoverState(null) (لا يوجد شيء مثل وظيفة endHover في هدف شريطك). الطريقة الصحيحة للتعامل مع هذا هي استخدام componentWillReceiveProps مثل هذا:

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

(لا تنس إضافة { isFooOver: monitor.isOver() } في وظيفة التجميع الخاصة بك)

أعلمني كيف سيسير الامر!

شكرا جزيلا على الشرح التفصيلي! سأخبرك :)

أخيرًا ، استخدمت نهج Redux ، لذلك قمت أساسًا بإرسال حدث من خلال مخفض عندما تم إطلاق حدث الإسقاط.

    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 التقييمات