React-dnd: "canDrop", ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ค ์š”์†Œ ์œ„์— ์žˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์— ๋งŒ๋“  2016๋…„ 04์›” 14์ผ  ยท  4์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: react-dnd/react-dnd

drop(props, monitor, component) ๋ฉ”์„œ๋“œ์—๋Š” ์„ธ ๊ฐœ์˜ ์ธ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ canDrop(props, monitor) ์—๋Š” component ์ธ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

ํ•ญ๋ชฉ์„ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ ์œ„๋กœ ๋“œ๋ž˜๊ทธํ•  ๋•Œ ํ•ด๋‹น ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ 50%(๋†’์ด)์˜ ๋‘ ๋ถ€๋ถ„์œผ๋กœ "๋ถ„ํ• "ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์•„์ดํ…œ์„ ๋†’์€ ๋ถ€๋ถ„์— ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ๊ทธ ์œ„์— ๋†“์„ ๊ฒƒ์ด๊ณ , ๋‚ฎ์€ ๋ถ€๋ถ„์— ๋†“์œผ๋ฉด ๊ทธ ๋’ค์— ๋†“์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ƒ๋‹จ/ํ•˜๋‹จ ๋ถ€๋ถ„์— ๋“œ๋กญ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์ด๋ฏธ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ง€๊ธˆ ์›ํ•˜๋Š” ๊ฒƒ์€ ๋งˆ์šฐ์Šค๊ฐ€ ์ƒ๋‹จ/ํ•˜๋‹จ ๋ถ€๋ถ„์— ์žˆ๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๊ทธ๋Ÿฌ๋‚˜ ๋“œ๋กญ ๋™์ž‘์ด ์•„๋‹Œ ํ˜ธ๋ฒ„ ๋™์ž‘ ์ค‘์—).

hover(props, monitor, component) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด DOM์— CSS ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ด์™€ ์œ ์‚ฌํ•œ ๊ตฌ์„ฑ ์š”์†Œ์— ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ์ž์ฒด์— ์†ํ•˜์ง€ ์•Š๊ณ  ํ•ด๋‹น ์ปจํ…์ŠคํŠธ ๋“ฑ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋ญ”๊ฐ€๋ฅผ ๋†“์น˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์•„. :)

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ข‹์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์•„์ด๋””์–ด๋Š” ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ƒํƒœ๊ฐ€ ์„ค์ •๋˜๋ฉด ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Foo๋ฅผ 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",
    });
  }
}

์œ ์ผํ•œ ์งˆ๋ฌธ์€ ๊ทธ ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•˜๋Š๋ƒ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค!

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

๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ setState ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” setFooHover ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์ƒ์˜ ์ด์œ ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

์œ„์—์„œ undecorate ๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ์ด์œ ๋Š” react-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๋ฅผ 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",
    });
  }
}

์œ ์ผํ•œ ์งˆ๋ฌธ์€ ๊ทธ ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•˜๋Š๋ƒ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค!

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

๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ setState ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” setFooHover ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์ƒ์˜ ์ด์œ ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

์œ„์—์„œ undecorate ๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ์ด์œ ๋Š” react-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 ๋“ฑ๊ธ‰