【问题标题】:React DnD showing entire list when draggingReact DnD 拖动时显示整个列表
【发布时间】:2017-07-03 05:45:40
【问题描述】:

我正在尝试使用ListListItemMaterial UI 集成React DnD,并且在拖动时,整个列表显示为拖动元素。我试图尽我所能理解这些示例,这就是我所拥有的

import React, { Component, PropTypes } from 'react';
import { Random } from 'meteor/random';
import LocalizedComponent from '/client/components/LocalizedComponent';
// MUI
import { List, ListItem } from 'material-ui/List';
// ---
import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
import { findDOMNode } from 'react-dom';

import HTML5Backend from 'react-dnd-html5-backend';


const itemSource = {
  beginDrag(props) {
    return {
      id: props.id,
      index: props.index
    };
  },
};

const itemTarget = {
  hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.onMoveItem(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  },
};


class SortableListComponent extends Component {

  render() {
    const { children, onMoveItem } = this.props;
    let index = 0;

    return (
      <List>
        { React.Children.map(children, child => React.cloneElement(child, {
          id: Random.id(),
          index: index++,
          onMoveItem: onMoveItem
        })) }
      </List>
    );
  }
}

SortableListComponent.propTypes = {
  onMoveItem: PropTypes.func.isRequired
};


class SortableListItemComponent extends Component {

  render() {
    const {
      id,
      index,
      isDragging,
      connectDragSource,
      connectDropTarget,
      onMoveItem,
      ...other
    } = this.props;
    const opacity = 1; // isDragging ? 0 : 1;

    return connectDragSource(connectDropTarget(
      <div style={{ opacity }}>
        <ListItem { ...other } disabled={ isDragging } />
      </div>
    ));
  }
}
SortableListItemComponent.propTypes = {
  connectDragSource: PropTypes.func.isRequired,
  connectDropTarget: PropTypes.func.isRequired,
  id: PropTypes.any.isRequired,
  index: PropTypes.number.isRequired,
  isDragging: PropTypes.bool.isRequired,
  onMoveItem: PropTypes.func.isRequired,
};


export const SortableList = DragDropContext(HTML5Backend)(SortableListComponent);

export const SortableListItem = DropTarget('SortableListItem', itemTarget, connect => ({
  connectDropTarget: connect.dropTarget(),
}))(DragSource('SortableListItem', itemSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging(),
}))(SortableListItemComponent));

基本上,我用List 代替SortableListListItem 代替SortableListItem,这就是我在拖动时看到的

我做错了什么?

编辑

例如,这里是一个示例用法

<SortableList>
  { actions.map((action, index) => (
    <SortableListItem id={ action.name } key={ index }
      primaryText={ (index + 1) + '. ' + action.name }
      onTouchTap={ this.handleActionEdit.bind(this, index) }
    />
  )) }
</SortableList>

<SortableList>
  { actions.map((action, index) => (
    <SortableListItem id={ action.name } key={ action.name }
      primaryText={ (index + 1) + '. ' + action.name }
      onTouchTap={ this.handleActionEdit.bind(this, index) }
    />
  )) }
</SortableList>

等等

它不会改变任何事情。

【问题讨论】:

  • 我也遇到了类似的问题,但似乎这个问题只发生在 chrome 而不是 Firefox 中

标签: reactjs meteor material-ui react-dnd


【解决方案1】:

我也遇到了类似的问题,发现这在 chrome 上无法正常工作。类似问题请参考https://github.com/react-dnd/react-dnd/issues/832。用户的以下评论帮助我找出了问题。

@kaiomagalhaes 在我的情况下,我遇到了这个问题,因为行的一个子元素(单元格内容)实际上具有大于行高的高度,但被可见性隐藏:隐藏在 css 中。所以 dragSource 有行的宽度和隐藏控件的高度。

希望对您有所帮助。

【讨论】:

    【解决方案2】:

    问题可能在于您如何将idkey 传递给您的子组件。使用 React dnd 生成随机 id 最终会出错。您应该做的是,您应该在数据中拥有每个项目的唯一 ID,在您的情况下为 children。一个例子在这里:https://github.com/react-dnd/react-dnd/blob/master/examples/04%20Sortable/Simple/Container.js#L17

    { React.Children.map(children, child => React.cloneElement(child, {
        id: child.id,
        key: child.id,
        index: index++,
        onMoveItem: onMoveItem
    })) }
    

    【讨论】:

    • 我没有该数据的 ID,它来自子文档数组。不过,我会尝试看看是否可以从数据中生成散列......但这并不能保证每个项目的唯一性。
    • 另外,在此处添加key,它应该与id相同。我已经更新了答案。
    • 我同时拥有keyid,它并没有改变任何事情。更新问题以反映这一点。
    • 尽量让 key 和 id 具有完全相同的值。例如:id={ action.name } key={ action.name }
    • 所以我明白了如何解决重影列表项问题,谢谢。然而,图像拖动仍然是整个列表!
    【解决方案3】:

    我还尝试使用 React-DND 制作可排序的 Material-UI &lt;List&gt; 并遇到了同样的问题,但快照是我的整个页面,而不仅仅是列表!

    仍在努力解决它,但我确实注意到,如果您使用常规的 &lt;div&gt; 而不是 &lt;ListItem /&gt;,问题就会消失。所以我担心这可能是 Material-UI 的怪癖。最简单的解决方案可能只是避免&lt;ListItem&gt;

    我确实想出了一个解决方法,如果我找到更好的解决方法,我会编辑我的答案:

    用法

    import SortableList from './SortableList';
    
    <SortableList
        items={[
            {id: 1, text: 'one'},
            {id: 2, text: 'two'},
            {id: 3, text: 'three'},
            {id: 4, text: 'four'},
        ]}
        onChange={items => {
            console.log(JSON.stringify(items));
            // flux action, etc. goes here
        }}
        listItemProps={(item, index) => {
            return {
                primaryText: item.name,
                hoverColor: 'green',
                // pass desired props to <ListItem> here
            };
        }}
    />
    

    SortableList.jsx

    import React, { Component, PropTypes } from 'react';
    import update from 'react-addons-update';
    
    import List, { ListItem } from 'material-ui/List';
    
    import { findDOMNode } from 'react-dom';
    import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
    import HTML5Backend from 'react-dnd-html5-backend';
    
    const SortableListTypes = {
        ITEM: 'ITEM',
    };
    
    class SortableListItem extends Component {
        render() {
            const { connectDragSource, connectDragPreview, connectDropTarget, ...rest } = this.props;
            return (
                <ListItem
                    id={this.props.id + '/' + this.props.index}
                    key={this.props.id + '/' + this.props.index}
                    ref={instance => {
                        if (!instance) {
                            return;
                        }
                        const node = findDOMNode(instance);
                        connectDragSource(node);
                        connectDropTarget(node);
    
                        const greatGrandChild = node.childNodes[0].childNodes[0].childNodes[0];
                        connectDragPreview(greatGrandChild);
                    }}
                    {...rest}
                />
            );
        }
    }
    
    SortableListItem = DropTarget(
        SortableListTypes.ITEM,
        {
            hover: (props, monitor, component) => {
                const dragIndex = monitor.getItem().index;
                const hoverIndex = props.index;
    
                if (dragIndex === hoverIndex) {
                    return;
                }
    
                const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
                const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
                const clientOffset = monitor.getClientOffset();
                const hoverClientY = clientOffset.y - hoverBoundingRect.top;
                if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
                    return;
                }
                if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
                    return;
                }
    
                props.moveItem(dragIndex, hoverIndex);
                monitor.getItem().index = hoverIndex;
            },
            drop: props => props.dropHandler()
        },
        (connect, monitor) => {
            return {
                connectDropTarget: connect.dropTarget(),
            }
        }
    )(DragSource(
        SortableListTypes.ITEM,
        {
            beginDrag: (props, monitor, component) => {
                return {
                    id: props.id,
                    index: props.index,
                }
            }
        },
        (connect, monitor) => {
            return {
                connectDragSource: connect.dragSource(),
                connectDragPreview: connect.dragPreview(),
            }
        }
    )(SortableListItem));
    
    class SortableList extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                items: _.clone(props.items),
            };
    
            this.moveItem = this.moveItem.bind(this);
            this.dropHandler = this.dropHandler.bind(this);
        }
    
        componentWillReceiveProps(nextProps) {
            this.setState({
                items: _.clone(nextProps.items),
            });
        }
    
        moveItem(fromIndex, toIndex) {
            const draggedItem = this.state.items[fromIndex];
    
            this.setState(update(this.state, {
                items: {
                    $splice: [
                        [fromIndex, 1],
                        [toIndex, 0, draggedItem],
                    ],
                },
            }));
        }
    
        dropHandler() {
            this.props.onChange(_.clone(this.state.items));
        }
    
        render() {
            return (
                <List>
                    {this.state.items.map((item, index) => (
                        <SortableListItem
                            key={item.id}
                            index={index}
                            moveItem={this.moveItem}
                            dropHandler={this.dropHandler}
                            {...this.props.listItemProps(item, index)}
                        />
                    ))}
                </List>
            )
        }
    }
    
    export default DragDropContext(HTML5Backend)(SortableList);
    

    SortableListItem.jsx

    import React, { Component, PropTypes } from 'react';
    
    import { ListItem } from 'material-ui/List';
    
    import { findDOMNode } from 'react-dom';
    import { DragSource, DropTarget } from 'react-dnd';
    
    const SortableListTypes = {
        ITEM: 'ITEM',
    };
    
    class SortableListItem extends Component {
        render() {
            const { connectDragSource, connectDragPreview, connectDropTarget, ...rest } = this.props;
            return (
                <ListItem
                    id={this.props.id + '/' + this.props.index}
                    key={this.props.id + '/' + this.props.index}
                    ref={instance => {
                        if (!instance) {
                            return;
                        }
                        const node = findDOMNode(instance);
                        connectDragSource(node);
                        connectDropTarget(node);
    
                        const greatGrandChild = node.childNodes[0].childNodes[0].childNodes[0];
                        connectDragPreview(greatGrandChild);
                    }}
                    {...rest}
                />
            );
        }
    }
    
    export default DropTarget(
        SortableListTypes.ITEM,
        {
            hover: (props, monitor, component) => {
                const dragIndex = monitor.getItem().index;
                const hoverIndex = props.index;
    
                if (dragIndex === hoverIndex) {
                    return;
                }
    
                const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
                const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
                const clientOffset = monitor.getClientOffset();
                const hoverClientY = clientOffset.y - hoverBoundingRect.top;
                if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
                    return;
                }
                if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
                    return;
                }
    
                props.moveItem(dragIndex, hoverIndex);
                monitor.getItem().index = hoverIndex;
            },
            drop: props => props.dropHandler()
        },
        (connect, monitor) => {
            return {
                connectDropTarget: connect.dropTarget(),
            }
        }
    )(DragSource(
        SortableListTypes.ITEM,
        {
            beginDrag: (props, monitor, component) => {
                return {
                    id: props.id,
                    index: props.index,
                }
            }
        },
        (connect, monitor) => {
            return {
                connectDragSource: connect.dragSource(),
                connectDragPreview: connect.dragPreview(),
            }
        }
    )(SortableListItem));
    

    绝不是一个完美的解决方案,但希望它有所帮助。如果您的项目没有id 属性,则需要在SortableList.render 中编辑SortableListItemkey 属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-08
      • 1970-01-01
      • 2020-05-05
      • 2022-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多