React-dnd: CustomDragLayer ๋Š๋ฆฐ ์„ฑ๋Šฅ

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

๋‚˜์—๊ฒŒ CustomDragLayer๊ฐ€ ๋‚˜์œ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚ฎ์€ fps์™€ ๊ฐ™๊ฑฐ๋‚˜ ์˜คํžˆ๋ ค ๋‚ด๊ฐ€ ๋Œ๊ณ  ์žˆ๋Š” ์ž„์˜์˜ ๊ฐœ์ฒด๊ฐ€ ์ง€์—ฐ๋˜๊ณ  ๋งค๋„๋Ÿฝ์ง€ ์•Š๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค.

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

react-dnd-html5-backend๋Š” ์‚ฌ์šฉ์ž ์ •์˜ DragLayer์—์„œ ์„ฑ๋Šฅ์ด ์ข‹์ง€ ์•Š์€ ๋ฐ˜๋ฉด react-dnd-touch-backend๋Š” ์„ฑ๋Šฅ์ด ์–‘ํ˜ธํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  29 ๋Œ“๊ธ€

๋‚˜๋„ ์ด๊ฒƒ์„ ๊ฒฝํ—˜ํ–ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๋Š” ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด์—์„œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ์ˆœ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๋”๋ผ๋„ ์˜คํ”„์…‹ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด ๋ชจ๋“  ๋‹จ์ผ ๋งˆ์šฐ์Šค ์ด๋™์—์„œ ์ตœ์†Œํ•œ ๋ฐ˜์‘ ์กฐ์ •์ด ํŠธ๋ฆฌ๊ฑฐ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์š”์†Œ๋ฅผ ํ‘œ์‹œํ•ด์•ผ ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ์—๋Š” ๋งค์šฐ ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด DragLayer.js ๊ฐ€ ์˜คํ”„์…‹ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ณ ์„ฑ๋Šฅ ๋ฐฉ์‹์œผ๋กœ ๋ณต์ œํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ฃผ์œ„๋ฅผ ์ง์ ‘ ์ด๋™ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ์˜ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

// PerformantDragLayer.js
/* eslint-disable */
import React, { Component, PropTypes } from 'react';
import shallowEqual from 'react-dnd/lib/utils/shallowEqual';
import shallowEqualScalar from 'react-dnd/lib/utils/shallowEqualScalar';
import isPlainObject from 'lodash/isPlainObject';
import invariant from 'invariant';
import checkDecoratorArguments from 'react-dnd/lib/utils/checkDecoratorArguments';
import hoistStatics from 'hoist-non-react-statics';

function layerStyles(isDragging) {
  return {
    position: 'fixed',
    pointerEvents: 'none',
    zIndex: 1,
    left: 0,
    top: 0,
    width: isDragging ? '100%' : 0,
    height: isDragging ? '100%' : 0,
    opacity: isDragging ? 1 : 0
  };
}

export default function DragLayer(collect, options = {}) {
  checkDecoratorArguments('DragLayer', 'collect[, options]', ...arguments);
  invariant(
    typeof collect === 'function',
    'Expected "collect" provided as the first argument to DragLayer ' +
    'to be a function that collects props to inject into the component. ',
    'Instead, received %s. ' +
    'Read more: http://gaearon.github.io/react-dnd/docs-drag-layer.html',
    collect
  );
  invariant(
    isPlainObject(options),
    'Expected "options" provided as the second argument to DragLayer to be ' +
    'a plain object when specified. ' +
    'Instead, received %s. ' +
    'Read more: http://gaearon.github.io/react-dnd/docs-drag-layer.html',
    options
  );

  return function decorateLayer(DecoratedComponent) {
    const { arePropsEqual = shallowEqualScalar } = options;
    const displayName =
      DecoratedComponent.displayName ||
      DecoratedComponent.name ||
      'Component';

    class DragLayerContainer extends Component {
      static DecoratedComponent = DecoratedComponent;

      static displayName = `DragLayer(${displayName})`;

      static contextTypes = {
        dragDropManager: PropTypes.object.isRequired
      }

      getDecoratedComponentInstance() {
        return this.refs.child;
      }

      shouldComponentUpdate(nextProps, nextState) {
        return !arePropsEqual(nextProps, this.props) ||
          !shallowEqual(nextState, this.state);
      }

      constructor(props, context) {
        super(props);
        this.handleOffsetChange = this.handleOffsetChange.bind(this);
        this.handleStateChange = this.handleStateChange.bind(this);

        this.manager = context.dragDropManager;
        invariant(
          typeof this.manager === 'object',
          'Could not find the drag and drop manager in the context of %s. ' +
          'Make sure to wrap the top-level component of your app with DragDropContext. ' +
          'Read more: http://gaearon.github.io/react-dnd/docs-troubleshooting.html#could-not-find-the-drag-and-drop-manager-in-the-context',
          displayName,
          displayName
        );

        this.state = this.getCurrentState();
      }

      componentDidMount() {
        this.isCurrentlyMounted = true;

        const monitor = this.manager.getMonitor();
        this.unsubscribeFromOffsetChange = monitor.subscribeToOffsetChange(
          this.handleOffsetChange
        );
        this.unsubscribeFromStateChange = monitor.subscribeToStateChange(
          this.handleStateChange
        );

        this.handleStateChange();
      }

      componentWillUnmount() {
        this.isCurrentlyMounted = false;

        this.unsubscribeFromOffsetChange();
        this.unsubscribeFromStateChange();
      }

      handleOffsetChange() {
        if (!this.isCurrentlyMounted) {
          return;
        }

        const monitor = this.manager.getMonitor();
        const offset = monitor.getSourceClientOffset();
        const offsetDiv = this.refs.offset;
        if (offset && offsetDiv) {
          offsetDiv.style.transform = `translate(${offset.x}px, ${offset.y}px)`;
        }
      }

      handleStateChange() {
        if (!this.isCurrentlyMounted) {
          return;
        }

        const nextState = this.getCurrentState();
        if (!shallowEqual(nextState, this.state)) {
          this.setState(nextState);
        }
      }

      getCurrentState() {
        const monitor = this.manager.getMonitor();
        return {
          collected: collect(monitor),
          isDragging: monitor.isDragging()
        };
      }

      render() {
        return (
          <div style={layerStyles(this.state.isDragging)}>
            <div ref='offset'>
              {this.state.isDragging && <DecoratedComponent {...this.props} {...this.state.collected} ref='child' />}
            </div>
          </div>
        );
      }
    }

    return hoistStatics(DragLayerContainer, DecoratedComponent);
  };
}

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// MyCustomDragLayer.js
import PerformantDragLayer from './PerformantDragLayer'
import React, {PropTypes} from 'react'

class CustomDragLayerRaw extends React.Component {
  static propTypes = {
    item: PropTypes.any,
    itemType: PropTypes.string
  }

  render () {
    const {item, itemType} = this.props
    return <div>{itemType}</div>
  }
}

export default PerformantDragLayer((monitor) => ({
  item: monitor.getItem(),
  itemType: monitor.getItemType()
})(CustomDragLayerRaw)

์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ๋งค์šฐ ๋ˆˆ์— ๋•๋‹ˆ๋‹ค.

drag-layer-default

drag-layer-performant

๋ฌผ๋ก  ๊ธฐ๋ณธ DragLayer ๊ฐ€ ๋” ์œ ์—ฐํ•ฉ๋‹ˆ๋‹ค. ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋” ์„ฑ๋Šฅ์ด ์ข‹์€ ๊ตฌํ˜„์ด ๋” ๋น ๋ฆ…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๋Š” ๋งค์šฐ ํ”ํ•œ ์ผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@gaearon ๊ทธ๋ ‡๊ฒŒ ์ „๋ฌธํ™”๋˜์—ˆ์ง€๋งŒ ํ›จ์”ฌ ๋” ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚œ ๊ตฌํ˜„์ด react-dnd์— ๋Œ€ํ•œ ๋Œ€์•ˆ์œผ๋กœ ํ†ตํ•ฉ๋˜๋Š” ๋ฐ ํฅ๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@choffmeister ์•ˆ๋…•ํ•˜์„ธ์š”. ๊ท€ํ•˜์˜ ๊ฒŒ์‹œ๋ฌผ์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋‚˜๋Š” ํ•˜๋ฃจ ์ „์— ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•˜๊ฒŒ ์‚ดํŽด๋ณด์•˜์ง€๋งŒ ๊ทธ๋•Œ์„œ์•ผ โ€‹โ€‹๊นŠ์ด ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ฒŒ์‹œํ•œ ์ฝ”๋“œ๋Š” ๊ตฌํ˜„ ๋‚ด์—์„œ ๋งค์šฐ ์ „๋ฌธํ™”๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด์— ํ•„์š”ํ•œ ์ค‘์š”ํ•œ ์ธก๋ฉด์„ ์ง€์ ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๊ทธ๋ฆฌ๊ณ  CustomDragLayerRaw๊ฐ€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€๋„ ๋งค์šฐ ์œ ์ตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ชจ๋“  ํ”Œ๋Ÿฌ๊ทธ์ธ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ๊ธฐํƒ€ ์ฝ”๋“œ๊ฐ€ ์†”๋ฃจ์…˜์˜ ์ผ๋ถ€์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ํ•ด์š”

๋‚˜๋Š” ์ฃผ์œ„๋ฅผ ์‹คํ—˜ํ•˜๊ณ  ์ž‘๋™ํ•˜๋„๋ก ๊ด€๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. "๋“œ๋ž˜๊ทธ ๊ณ ์ŠคํŠธ"์— ์›ํ•˜๋Š” ์ฝ˜ํ…์ธ ๊ฐ€ '์˜คํ”„์…‹' div์— ์žˆ์–ด์•ผ ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ตœ์ ์˜ ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด์ง€๋งŒ ๋‚ด ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋“œ๋ž˜๊ทธํ•œ ์ฝ˜ํ…์ธ ์— ๋น„์Šทํ•œ div๋ฅผ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„์ง ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด ํ•ญ๋ชฉ์„ ๋“œ๋ž˜๊ทธ ํ•ญ๋ชฉ๊ณผ ๋™์ผํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ์•Š์•˜์ง€๋งŒ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด ํ•ญ๋ชฉ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์€ ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์„ฑ๋Šฅ์€ ๋งค์šฐ ์ข‹์ง€๋งŒ ์ปจํ…Œ์ด๋„ˆ ์™ธ๋ถ€๋กœ ๋Œ ๋•Œ๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ๋ชฉ ์ฃผ์œ„๋ฅผ ์›์œผ๋กœ ๋“œ๋ž˜๊ทธํ•˜๋ฉด ์„ฑ๋Šฅ๋„ ์ด์ „์˜ ๊ฒฝ์šฐ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค(์ฆ‰, 4๊ฐœ์˜ ํ•ญ๋ชฉ ๋ชฉ๋ก์ด ์žˆ๊ณ  4๋ฒˆ์งธ ํ•ญ๋ชฉ ์˜์—ญ์—์„œ ๋งˆ์šฐ์Šค๋ฅผ ๋“œ๋ž˜๊ทธํ•œ ์ƒํƒœ๋กœ ์œ ์ง€). ๊ทธ๋Ÿฌ๋‚˜ 4๊ฐœ ์š”์†Œ ๋ชจ๋‘์— ๊ฑธ์ณ ๋งˆ์šฐ์Šค๋กœ ์›์„ ๊ทธ๋ฆฌ๊ณ  ๋“œ๋ž˜๊ทธํ•˜๋ฉด ์ด์ „๊ณผ ๊ฐ™์ด ์—ฌ์ „ํžˆ ์ง€์—ฐ๋˜์ง€๋งŒ ์•ฝ๊ฐ„์˜ fps ๊ฐœ์„ ์ด ์žˆ์Šต๋‹ˆ๋‹ค(๊ทธ๋ž˜๋„ ์—ฌ์ „ํžˆ ์ง€์—ฐ๋จ). ๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ตฌํ˜„์€ ์ข‹์€ ์‹œ์ž‘์ž…๋‹ˆ๋‹ค. ์ ์–ด๋„ ํ•œ ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋‹ค๋ฅธ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋“œ๋ž˜๊ทธํ•˜๋ ค๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ์—๋Š” UX๋ฅผ ํ™•์‹คํžˆ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ง€์—ฐ์ด ์ •ํ™•ํžˆ ๋ฌด์—‡์„ ์˜๋ฏธํ•ฉ๋‹ˆ๊นŒ?

  1. FPS๊ฐ€ ๋‚ฎ์•„์„œ ๋ฒ„๋ฒ…๊ฑฐ๋ฆฌ๊ฑฐ๋‚˜
  2. ์š”์†Œ๊ฐ€ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์›€์ง์ด์ง€๋งŒ ์›€์ง์ด๋Š” ๋™์•ˆ ํ•ญ์ƒ ๋งˆ์šฐ์Šค ์›€์ง์ž„๋ณด๋‹ค ์•ฝ๊ฐ„์˜ ๊ฑฐ๋ฆฌ๊ฐ€ ๋–จ์–ด์ ธ ์žˆ์Šต๋‹ˆ๊นŒ?

๋‚˜๋Š” ๋‘˜ ๋‹ค ๋งํ•  ๊ฒƒ์ด๋‹ค

๋” ์‚ฌ์šฉํ•˜๋ฉด์„œ ์†”๋ฃจ์…˜์ด ์‹ค์ œ๋กœ ์ง€์—ฐ์„ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ์กฐ์šฉํ•œ ์•ฝ๊ฐ„์˜ ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ์นด๋“œ div๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์€ ์ฒ˜์Œ์— ๋ฌธ์„œ์˜ ์˜ˆ์™€ ๋น„๊ตํ•˜์—ฌ ์—„์ฒญ๋‚œ fps ํ–ฅ์ƒ์„ ์ œ๊ณตํ–ˆ์ง€๋งŒ ๋‚ด div๋ฅผ ๋” ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค๋ฉด ์ด๊ฒƒ์ด ์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ง€์—ฐ๋˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์˜๋ฏธ๋ก ์  UI๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๊ฑฐ๋‚˜ dnd์— ๋ฐ˜์‘ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด์˜ ์„ฑ๋Šฅ์„ ๋†’์ด๋Š” ๋‹ค๋ฅธ ์ตœ์ ์˜ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค๋ฉด ์ •๋ง ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ž์‹ ์—๊ฒŒ ๋’ค๋–จ์–ด์ง€๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‚ด๋ถ€์— ๋งŽ์€ ์š”์†Œ๊ฐ€ ํฌํ•จ๋œ Semantic UI ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์ž๋ฆฌ ํ‘œ์‹œ์ž ์นด๋“œ์—๋Š” 2๊ฐœ์˜ ์ œ๋ชฉ, 5๊ฐœ์˜ ์˜๋ฏธ๋ก ์  "๋ ˆ์ด๋ธ”", 3๊ฐœ์˜ ์˜๋ฏธ๋ก ์  "์•„์ด์ฝ˜" ๋ฐ ์•„๋ฐ”ํƒ€ ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

http://react-trello-board.web-pal.com/ ์—์„œ ์ด ๋‹ค๋ฅธ ์˜ˆ๋ฅผ ์ถ”๊ฐ€๋กœ ํ…Œ์ŠคํŠธํ•œ ํ›„(๊ตฌํ˜„์€ choffmeister๊ฐ€ ์ด์ „์— ๊ฒŒ์‹œํ•œ ๊ฒƒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•จ) ๋ณต์žกํ•œ div๋ฅผ ๋“œ๋ž˜๊ทธํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์—ฌ์ „ํžˆ ์ง€์—ฐ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. . ์ฝ”๋“œ๋ฅผ ๋” ์‹คํ—˜ํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ๋‚ด๊ฐ€ ๋‚ด๋ฆฐ ๊ฒฐ๋ก ์ด๋ผ ๊ธฐ์ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์‰ฝ๊ฒŒ ๊ณ ์น  ์ˆ˜ ์žˆ๋Š” ๋‚ด div์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์—ฌ์ „ํžˆ ๋ฌธ์„œ์˜ ์˜ˆ์ œ์™€ ๋งค์šฐ ์œ ์‚ฌํ•œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๊ฒƒ์„ ๊ฝค ์‹ฌํ•˜๊ฒŒ ๊ฒฝํ—˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์€ ๋งˆ์šฐ์Šค๊ฐ€ ์›€์ง์ผ ๋•Œ๋งˆ๋‹ค ๋ณ€ํ™˜ ๊ณ„์‚ฐ์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๊นŒ?

PureComponents์—์„œ๋„ ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. Chrome React Dev ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ '์—…๋ฐ์ดํŠธ ๊ฐ•์กฐ' ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง„๋‹จํ•˜๋ฉด currentOffset ์†Œํ’ˆ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์•ฑ์˜ ์ตœ์ƒ์œ„ ์ˆ˜์ค€์— ์–ด๋–ค ํ•ญ๋ชฉ์ด ๋“œ๋ž˜๊ทธ๋˜๋Š”์ง€ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•œ DragLayer๊ฐ€ ์ด๋ฏธ ์žˆ๋”๋ผ๋„ ์ด ์†Œํ’ˆ์„ ์ „๋‹ฌํ•˜๋Š” DragLayer์— ๋“œ๋ž˜๊ทธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ž์ฒด๋งŒ ํฌํ•จ๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

@choffmeister ์˜ ์†”๋ฃจ์…˜์„ ์ ์šฉํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜์ง€๋Š” ์•Š์•˜์ง€๋งŒ ๋ฌธ์ œ๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด ๋“œ๋ž˜๊ทธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ๊ณตํ†ต์ ์ธ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ์ด์™€ ๊ฐ™์€ ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด ์†Œํ’ˆ์„ ์ „๋‹ฌํ•˜๋Š” DragLayer์—๋Š” ์•ฑ์˜ ์ตœ์ƒ์œ„ ์ˆ˜์ค€์— ์ด๋ฏธ DragLayer๊ฐ€ ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋“œ๋ž˜๊ทธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ž์ฒด๋งŒ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŒ”๋กœ์šฐ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ๋„ค์š”! ๋‚ด ์•ฑ์—๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€์—๋งŒ ํ•ด๋‹นํ•˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ๋“œ๋ž˜๊ทธ ์ค‘์ธ ํ•ญ๋ชฉ์„ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ง€์ • ๋“œ๋ž˜๊ทธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ์ „์— ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๊ฐ€ ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉ์ž ์ง€์ • ๋“œ๋ž˜๊ทธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋™์ผํ•œ ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ ์ด๊ฒƒ์ด ๋‚ด ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์ตœ์ƒ์œ„ ์ˆ˜์ค€์— ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„์˜ ์›€์ง์ผ ๋•Œ๋งˆ๋‹ค ์ด์— ๋Œ€ํ•œ ์†Œํ’ˆ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋งŽ์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€ ๊ตฌ์„ฑ ์š”์†Œ ์—…๋ฐ์ดํŠธ.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๋ฅผ ๋‹จ์ผ ๋ฒ™์–ด๋ฆฌ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋‹จ์ˆœํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜์žˆ๋Š” ํ•œ ์ง€์—ฐ์„ ์ผ์œผํ‚ค๋Š” ๊ฒƒ์€ ์ผ์ •ํ•œ x/y ๊ณ„์‚ฐ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์•„์ง ์œ„์—์„œ @choffmeister ๊ฐ€ ํ•˜๋Š” ์ผ์„ ์ž˜ ๋”ฐ๋ฅด์ง€ ์•Š์ง€๋งŒ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@gaearon ์ด๊ฒƒ์€ lib์— ๋Œ€ํ•ด ๊ฝค ๊ดœ์ฐฎ์€ ๋ฌธ์ œ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์ œ์•ˆ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

react-dnd-html5-backend๋Š” ์‚ฌ์šฉ์ž ์ •์˜ DragLayer์—์„œ ์„ฑ๋Šฅ์ด ์ข‹์ง€ ์•Š์€ ๋ฐ˜๋ฉด react-dnd-touch-backend๋Š” ์„ฑ๋Šฅ์ด ์–‘ํ˜ธํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ป”ํ•˜์ง€๋งŒ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

let updates = 0;
@DragLayer(monitor => {
  if (updates++ % 2 === 0) {
    return {
      currentOffset: monitor.getSourceClientOffset()
    }
  }
  else {
    return {}
  }
})

๋˜ ๋‹ค๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ react-throttle-render ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DragLayer์˜ ๋ Œ๋”๋ง ๋ฐฉ๋ฒ•์„ ์กฐ์ ˆํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

https://www.npmjs.com/package/react-throttle-render

@aks427 ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์‹œ๋„ํ•ด ๋ณด๊ณ  ๋” ๊ฐœ์„ ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค(์œ„์™€ ๊ฐ™์€ ์‚ฌ์šฉ์ž ์ง€์ • ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์— ๋งŽ์€ ๋„์›€์ด ๋˜์—ˆ์ง€๋งŒ ์ „๋ถ€๋Š” ์•„๋‹˜)

@dobesv ๋ฅผ react-dnd-touch-backend๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋˜ํ•œ react-throttle-render๋ฅผ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ์ข‹์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์˜ˆ, ํ„ฐ์น˜ ๋ฐฑ์—”๋“œ๊ฐ€ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๋Œ์–ด์„œ ๋†“๊ธฐ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@dobesv ์šฐ๋ฆฌ๋Š” ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ํŒŒ์ผ์— https://react-dropzone.js.org/๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. :) ์•„๋งˆ๋„ ๋‹น์‹ ์—๊ฒŒ๋„ ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@dobesv ๋Š” ๋‚ด ์˜๊ฒฌ์„ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค. react-dropzone์€ ๋“œ๋กญ ํŒŒ์ผ๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ๊ฒฝ์šฐ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํŒŒ์ผ์ด ๋ธŒ๋ผ์šฐ์ € ์™ธ๋ถ€์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋“œ๋กญ ํŒŒ์ผ๋งŒ ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋ณ„๊ฑฐ ์•„๋‹Œ ๊ฒƒ ๊ฐ™์ง€๋งŒ ์„ฑ๋Šฅ์ด ์ข‹์€ ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด ์ˆ˜์ •์„ ์‚ฌ์šฉํ•˜๊ณ 
๋ Œ๋”๋ง ์ˆ˜๋ฅผ ์ œํ•œํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด ์ฝ”๋“œ๋ฅผ ๋ณด๋Š” ๋ฐ ๋งŽ์€ ์‹œ๊ฐ„์ด
์•ฑ์— ์˜ํ•ด ํ˜ธ์ถœ๋˜๊ณ (์ปดํฌ๋„ŒํŠธ -> PureComponent ์ „ํ™˜์ด ๋งŽ์Œ)
๊ฒฐ๊ตญ ๋‚˜๋Š” react-throttle-render๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”์กฐ์ฐจ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

PureComponent๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋‹ค์Œ์„ ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
์ด ์Šค๋ ˆ๋“œ์—์„œ ์‰ฌ์šด ์ˆ˜์ •์„ ์ฐพ๋Š” ๋Œ€์‹  ์ฝ”๋“œ๋ฅผ ์ตœ๋Œ€ํ™”ํ•˜์‹ญ์‹œ์˜ค.
๋ Œ๋”๋ง์ด ๋Š๋ฆฝ๋‹ˆ๋‹ค! ๋‹ค์Œ์—์„œ ์„ฑ๋Šฅ ๋ชจ๋ธ์„ ๊ฐœ์„ ํ•˜๋ฉด ๋” ๋งŽ์€ ๋‚ด์šฉ์„ ๊ฒŒ์‹œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜๋„!

>

@framerate PureComponents ๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์กฐ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋งˆ์šฐ์Šค๋ฅผ ์›€์ง์ผ ๋•Œ๋งˆ๋‹ค ์‹คํ–‰ํ•˜๋Š” ๋ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“ค๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. Chrome DevTools๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ur ์•ฑ์„ ํ”„๋กœํŒŒ์ผ๋งํ•˜๊ณ  CPU๋ฅผ 4X๋กœ ์กฐ์ ˆํ•˜๋ฉด ๋ฏธ๋“œ์—”๋“œ ๋ชจ๋ฐ”์ผ ์žฅ์น˜์˜ ๊ฒฝ์šฐ ์‹ค์ œ ์†๋„๊ฐ€ ๋Š๋ ค์ง‘๋‹ˆ๋‹ค.

ํ›„์†๊ณผ ๋“œ๋ž˜๊ทธ ์„ฑ๋Šฅ์— ์–ด๋ ค์›€์„ ๊ฒช๊ณ  ์žˆ๊ณ  @choffmeister ์†”๋ฃจ์…˜์—์„œ์™€ ๊ฐ™์ด ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์ง€ ์•Š์€ ์‚ฌ๋žŒ์„ ์œ„ํ•ด:

let subscribedToOffsetChange = false;

let dragPreviewRef = null;

const onOffsetChange = monitor => () => {
  if (!dragPreviewRef) return;

  const offset = monitor.getSourceClientOffset();
  if (!offset) return;

  const transform = `translate(${offset.x}px, ${offset.y}px)`;
  dragPreviewRef.style["transform"] = transform;
  dragPreviewRef.style["-webkit-transform"] = transform;
};

export default DragLayer(monitor => {
  if (!subscribedToOffsetChange) {
    monitor.subscribeToOffsetChange(onOffsetChange(monitor));
    subscribedToOffsetChange = true;
  }

  return {
    itemBeingDragged: monitor.getItem(),
    componentType: monitor.getItemType(),
    beingDragged: monitor.isDragging()
  };
})(
  class CustomDragLayer extends React.PureComponent {
    componentDidUpdate(prevProps) {
      dragPreviewRef = this.rootNode;
    }

    render() {
      if (!this.props.beingDragged) return null;
      return (
        <div
          role="presentation"
          ref={el => (this.rootNode = el)}
          style={{
            position: "fixed",
            pointerEvents: "none",
            top: 0,
            left: 0
          }}
        >
          {renderComponent(
            this.props.componentType,
            this.props.itemBeingDragged
          )}
        </div>
      );
    }
  }
);

@stellarhoof ์ข‹์€ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋ถˆํ–‰ํžˆ๋„ ์†”๋ฃจ์…˜์€ IE11์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. subscribeToOffsetChange ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ „๋‹ฌํ•œ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹คํ–‰ํžˆ subscribeToOffsetChange ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋Œ€์‹  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ง‘๊ธฐ ๋‚ด๋ถ€์— ๋ฒˆ์—ญ์„ ์„ค์ •ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

let dragLayerRef: HTMLElement = null;

const layerStyles: React.CSSProperties = {
  position: 'fixed',
  pointerEvents: 'none',
  zIndex: 100,
  left: 0,
  top: 0,
  width: '100%',
  height: '100%',
};

const dragLayerCollector = (monitor: DragLayerMonitor) => {
  if (dragLayerRef) {
    const offset = monitor.getSourceClientOffset() || monitor.getInitialClientOffset();

    if (offset) {
      dragLayerRef.style["transform"] = `translate(${offset.x}px, ${offset.y}px)`;
    } else {
      dragLayerRef.style["display"] = `none`;
    }
  }

  return {
    item: monitor.getItem(),
    isDragging: monitor.isDragging(),
  };
};

export default DragLayer(dragLayerCollector)(
  (props): JSX.Element => {
    if (!props.isDragging) {
      return null;
    }

    return (
      <div style={layerStyles}>
        <div ref={ (ref) => dragLayerRef = ref }>test</div>
      </div>
    );
  }
);

@stellarhoof renderComponent ์ด ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ๋” ํฐ ํŒŒ์ผ์˜ ์ผ๋ถ€์˜€์Šต๋‹ˆ๊นŒ? (React๋„ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์Œ)

@choffmeister dnd์˜ ์ดํ›„ ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜์…จ์Šต๋‹ˆ๊นŒ? ์ปจํ…์ŠคํŠธ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด ๊ตฌํ˜„์ด ์ค‘๋‹จ๋œ ๊ฒƒ์œผ๋กœ ๋ณด์ด๋ฉฐ ์ด๋ฅผ ์‹œ๋„ํ•˜๊ณ  @stellarhoof ๊ฐ€ ์ˆ˜ํ–‰ํ•œ ์ž‘์—…๊ณผ ๋น„๊ตํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

@stellarhoof ์ข‹์€ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋ถˆํ–‰ํžˆ๋„ ์†”๋ฃจ์…˜์€ IE11์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. subscribeToOffsetChange ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ „๋‹ฌํ•œ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹คํ–‰ํžˆ subscribeToOffsetChange ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋Œ€์‹  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ง‘๊ธฐ ๋‚ด๋ถ€์— ๋ฒˆ์—ญ์„ ์„ค์ •ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

let dragLayerRef: HTMLElement = null;

const layerStyles: React.CSSProperties = {
  position: 'fixed',
  pointerEvents: 'none',
  zIndex: 100,
  left: 0,
  top: 0,
  width: '100%',
  height: '100%',
};

const dragLayerCollector = (monitor: DragLayerMonitor) => {
  if (dragLayerRef) {
    const offset = monitor.getSourceClientOffset() || monitor.getInitialClientOffset();

    if (offset) {
      dragLayerRef.style["transform"] = `translate(${offset.x}px, ${offset.y}px)`;
    } else {
      dragLayerRef.style["display"] = `none`;
    }
  }

  return {
    item: monitor.getItem(),
    isDragging: monitor.isDragging(),
  };
};

export default DragLayer(dragLayerCollector)(
  (props): JSX.Element => {
    if (!props.isDragging) {
      return null;
    }

    return (
      <div style={layerStyles}>
        <div ref={ (ref) => dragLayerRef = ref }>test</div>
      </div>
    );
  }
);

์ด๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ–ˆ์Šต๋‹ˆ๋‹ค!
์ด ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค: 9.4.0

๋‹ค๋ฅธ ์†”๋ฃจ์…˜์€ ์ด์ „ ๋ฒ„์ „์—์„œ๋งŒ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์–˜๋“ค ์•„,

๊ณ„์† ์ง„ํ–‰ํ•˜๊ธฐ ์ „์— CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ตœ์†Œํ•œ transition ์†์„ฑ์„ ์ œ๊ฑฐํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

์ „ํ™˜ ์†์„ฑ onStart๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋‹ค์‹œ onEnd์— ์ถ”๊ฐ€ํ•œ ํ›„ ๋ชจ๋“  ๊ฒƒ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ›„ํฌ(useDragLayer ํ›„ํฌ)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์—ฌ๊ธฐ์— ์ฐฉ๋ฅ™ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด: ์ €๋Š” ํŠนํžˆ ํ›„ํฌ ๊ตฌํ˜„์— ๋Œ€ํ•œ ํ‹ฐ์ผ“์„ ์—ด์—ˆ์œผ๋ฉฐ ์—ฌ๊ธฐ์— ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ์ œ์•ˆ์ด ์žˆ์Šต๋‹ˆ๋‹ค: https://github.com/react-dnd/react-dnd/issues/2414

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰