【问题标题】:React-dnd - Not sure how to get drag and drop to fire for nested components of the same typeReact-dnd - 不确定如何为相同类型的嵌套组件触发拖放操作
【发布时间】:2015-11-11 20:24:46
【问题描述】:

我正在尝试构建一个可以使用 react-dnd 完全拖动的反应嵌套列表组件。我有一个包含项目组件的菜单组件,每个项目还可以包含其他项目组件。不幸的是,我不知道如何使具有可拖动子节点的节点或其任何子节点。现在我得到了没有 childrendraggable 的节点,这是一个好的开始,但随后失败了:

TypeError: 节点未定义

任何时候我试图拖动任何孩子。我使用了 react-dnd simple sortable 示例作为参考,但它不包含任何嵌套。

这是我目前所拥有的:

Menu.js

//React DnD
var DragDropContext = require('react-dnd').DragDropContext;
var HTML5Backend = require('react-dnd-html5-backend');

//Item
var Item = require('./item');

var Menu = React.createClass({
    getInitialState() {
        return {
            currentNode: this.props.data,
            items: [],
        };
    },
    _clicked(child) {
        this.setState({
            currentNode: child,
        });
    },
    componentDidMount() {
        this._updateData();
    },
   _updateData: function(list) {
        var $this = this;

        if(_.isUndefined(list)){
            var children = $this.props.data.children;
        }
        else{
            if(_.isEmpty(list.children)){
                var children = null;
            }
            else{
                var children = list.children;
            }
        }

        if(children != null){
            var items = children.map(function(item, i) {
                return (<Item
                    key={item.id}
                    id={item.id}
                    child={item}
                    showChildren={this.props.showFirstChildren}
                    clickable={true}
                    onClick={$this._updateData}
                    swapItems={this.swapItems}
                />);
            }.bind(this));

            
            if(!_.isUndefined(list)){
                $this.setState({
                    currentNode: list,
                });
            }
            
            $this.setState({ 
                items: items,
            });
            
        }
    },
    compareItems: function(item1, item2){
        return item1.position - item2.position;
    },
    swapItems: function(id1, id2) {
        var $this = this;

        var items = this.state.currentNode.children;
        var item1 = items.filter(function(i){return i.id === id1})[0];
        var item2 = items.filter(function(i){return i.id === id2})[0];

        var item1Pos = item1.position;
        item1.position = item2.position;
        item2.position = item1Pos;

        items.sort(this.compareItems);

        var newItems = items.map(function(item, i) {
            return (<Item
                key={item.id}
                id={item.id}
                child={item}
                showChildren={this.props.showFirstChildren}
                clickable={true}
                onClick={$this._updateData}
                swapItems={this.swapItems}
            />);
        }.bind(this));

        this.setState({
            items: newItems,
        });
    },
    render() {
        return (
            <div>
                {this.state.currentNode.name}
                <ul>
                    {this.state.items}
                </ul>
            </div>
        );
    },
});

module.exports = DragDropContext(HTML5Backend)(Menu);

Item.js

var React = require('react');
var ReactDnD = require('react-dnd');

var ItemTypes = {
  ITEM: 'item'
};

var itemSource = {
  beginDrag: function (props) {
    return {
        id: props.id,
    };
  }
};

var itemTarget = {
    hover: function(props, monitor) {
        var draggedId = monitor.getItem().id;
        if (draggedId !== props.id) {
            props.swapItems(draggedId, props.id);
        }
    }
};

var Item = React.createClass({

    propTypes: {
        connectDropTarget: React.PropTypes.func,
        connectDragSource: React.PropTypes.func,
        isDragging: React.PropTypes.bool,
        id: React.PropTypes.any,
        swapItems: React.PropTypes.func,
    },
    _clicked(child) {
        this.props.onClick(child);
    },
    render() {
        var $this = this;

        var connectDragSource = $this.props.connectDragSource;
        var isDragging = $this.props.isDragging;
        var connectDropTarget = $this.props.connectDropTarget;
        var child = this.props.child;


        var childrenWrapper = null;
        if (child.children && this.props.showChildren) {
            var children = child.children.map(function(item, i) {
                return (<Item
                    key={item.id}
                    id={item.id}
                    showChildren={false}
                    child={item}
                    onClick={$this._clicked}
                    clickable={true}

                    //passing these items along because otherwise it throws errors
                    connectDropTarget={$this.props.connectDropTarget}
                    connectDragSource={$this.props.connectDragSource}
                    swapItems={$this.props.swapItems}
                />);
            });

            childrenWrapper = (
                <ul>
                    {children}
                </ul>
            );
        }

        var style = {
            cursor: 'move',
            opacity: this.props.isDragging ? 0 : 1
        };

        if (child.children && this.props.clickable) {

            return connectDragSource(connectDropTarget(
                 <li style={style} key={child.id} ><a onClick={$this._clicked.bind(null, child)}>{child.title}</a>{childrenWrapper}</li>
            ));
        } else {

            return connectDragSource(connectDropTarget(
                 <li style={style} key={child.id}><a>{child.title}{childrenWrapper}</a></li>
            ));
        }
    },
});


var DragSourceDecorator = ReactDnD.DragSource(ItemTypes.ITEM, itemSource,
    function(connect, monitor) {
        return {
            connectDragSource: connect.dragSource(),
            isDragging: monitor.isDragging()
        };
});

var DropTargetDecorator = ReactDnD.DropTarget(ItemTypes.ITEM, itemTarget,
    function(connect, monitor) {
        return {
            connectDropTarget: connect.dropTarget(),
        };
});

module.exports = DropTargetDecorator(DragSourceDecorator(Item));

似乎嵌套项目没有正确传递 dragSource 和 dropTarget 因为当我查看我的 React DevTools 浏览器扩展中的结构时它们没有被包装,但我不确定因为我觉得 react-dnd 应该采取关心那个。在此先感谢,任何指导将不胜感激。

【问题讨论】:

标签: javascript reactjs nested-lists


【解决方案1】:

当我尝试在拖动结束之前操作树时,我遇到了同样的问题。 IE。在 dragEnd() 之前更改叶子顺序会给我同样的错误。但是plain list 工作得很好。 我在 DnD 期间停下来改变树(我们的设计师说 - 这不是很好的 UX:跳转到鼠标指针子树)。 所以我的解决方案:在 DnD 期间,只需更改目标和源子树的类(但不要更改叶子顺序),例如:

  .source-dnd {
    opacity: 0.2;
  }

  .target-dnd-add {
    border: 1px dotted #aaa;
  }

  .target-dnd-after {
    border-bottom: 1px dotted #aaa;
  }

  .target-dnd-before {
    border-top: 1px dotted #aaa;
  }

也别忘了monitor.isOver({ shallow: true })

【讨论】:

    【解决方案2】:

    您不必将“connectDropTarget”和“connectDropTarget”传递给嵌套项目。相反,您必须将 - 包装在 Item 的 render 方法中,而不是

       return (<Item
               key={item.id}...
    

       return (<WrappedItem
              key={item.id}… 
    

    而不是

       module.exports = DropTargetDecorator(DragSourceDecorator(Item));
    

       var WrappedItem = DropTargetDecorator(DragSourceDecorator(Item));
       module.exports = WrappedItem;
    

    【讨论】:

      猜你喜欢
      • 2021-06-17
      • 2019-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-18
      • 1970-01-01
      相关资源
      最近更新 更多