我试图在不使用任何外部库的情况下实现拖放的简单网格,但是我遇到了一些问题。
首先,我似乎无法使用占位符;我不想将项目彼此交换,我想在用户将执行放置的项目之间创建某种空白空间。 其次,我只能将项目放在列表的末尾。
似乎React + Plain HTML5 DnD API没有很多教程,但在this tutorial之后我尝试通过在类之前声明它然后在onDragOver和OnDragEnd中使用它来设置占位符方法,但它一直在叫我一些关于
的东西Failed to execute 'insertBefore' on 'Node': parameter 1 is not of type 'Node
此外,在教程中,他将onDragOver设置为ul元素,并且不处理任何onDrop事件,而我在项目上使用onDragOver并在网格容器上调用onDrop。我是这样做的还是我的grid_container只使用onDragOver而不是onDrop?
答案 0 :(得分:1)
我认为以下代码
onDrop = (e) => {
e.currentTarget.appendChild(this.state.dragged);
}
应该是
onDragEnd = (e) => {
e.currentTarget.appendChild(this.state.dragged);
}
注意函数名称。因为这是组件中调用的内容
onDragEnd={this.onDragEnd}
但这只是问题的一部分。
您的onDragEnd
函数正在尝试将拖动的对象追加到自身。伊克。这不起作用,你不应该试图修改dom。让反应修改它。
所以...... react正在显示this.state.items中的数组。在onDragEnd
或onDragOver
中,您需要更新this.state.items以反映订单,然后让反应重新呈现并显示新订单。
我在github页面上有一个工作示例,显示HTML dnd和react-dnd工作。
具有所有善良的component。请参阅src文件夹中的htmlList.js。
当拖动的项目经过另一个项目时,我采用了更新状态数组的方法。所以状态在dragOver中更新。
这是组件的代码。注意,显示的项目数组存储在父组件中,请参阅updateState
函数:
此外,还有一个shouldComponentUpdate
,因此只有在订单发生变化时才会重新呈现。
注意:我没有使用占位符,因为它不需要。当您将项目拖动到新位置时,整个列表会即时更新。
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import HtmlListItem from './HtmlListItem'
import './List.css'
export default class ListState extends Component {
static propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
sortIndex: PropTypes.number.isRequired,
})
).isRequired,
handleDataChanged: PropTypes.func.isRequired,
}
static defaultProps = {
data: {},
}
constructor(props) {
super(props)
this.state = {
draggedOverId: undefined,
beingDragged: undefined,
}
this.updateState = this.updateState.bind(this)
this.dragStart = this.dragStart.bind(this)
this.dragOver = this.dragOver.bind(this)
this.dragEnd = this.dragEnd.bind(this)
}
shouldComponentUpdate(nextProps, nextState) {
// only update when the item being hovered changes,
if (nextState.draggedOverId === this.state.draggedOverId) return false
return true
}
updateState(houses, draggedOverId) {
this.setState({ draggedOverId: draggedOverId, beingDragged: draggedOverId })
// update the sortIndex to show the new order
houses.forEach((house, i) => {
house.sortIndex = i
})
// Tell the parent there is a new order
this.props.handleDataChanged(houses)
}
dragStart(e) {
// Update our state with the item that is being dragged
this.setState({ beingDragged: Number(e.target.dataset.id) })
e.dataTransfer.effectAllowed = 'move'
}
dragOver(e) {
e.preventDefault()
// ignore when dragging over the list container
if (e.target.className === 'list') return
let from = this.state.beingDragged
let to = Number(e.target.dataset.id)
this.setState({ draggedOverId: to })
// reorder the array with the current hover position
let items = this.props.data
items.splice(to, 0, items.splice(from, 1)[0])
this.updateState(items, to)
}
dragEnd() {
// Update state to force removal of the class used for highlighting
this.updateState(this.props.data, undefined)
}
render() {
const { data } = this.props
const { draggedOverId } = this.state
const HtmllistItems = data.map((house, i) => {
// highlight the new position
let dragClass = i === draggedOverId ? 'listitem-over' : ''
return (
<HtmlListItem
key={house.id}
dataId={i}
className={dragClass}
text={house.name}
dragStart={this.dragStart}
dragEnd={this.dragEnd}
/>
)
})
return (
<ul className="list" onDragOver={this.dragOver}>
{HtmllistItems}
</ul>
)
}
}