I think the following code
onDrop = (e) => {
e.currentTarget.appendChild(this.state.dragged);
}
should be
onDragEnd = (e) => {
e.currentTarget.appendChild(this.state.dragged);
}
Notice the function name. Since that is what is called in the component
onDragEnd={this.onDragEnd}
That is only part of the issue though.
Your onDragEnd function is trying to append the dragged object to itself. eek. That won't work, and you shouldn't be trying to modify the dom. Let react modify it.
So...react is displaying the array that is in this.state.items. In onDragEnd or onDragOver you need to update this.state.items to reflect the order, then let react re-render and show the new order.
A way to make it work
I have a working example on my github page that shows both HTML dnd and react-dnd working.
The github repo
A working example
The component with all the dnd goodness. See htmlList.js in the src folder.
I took the approach of updating the array in state as the dragged item passes over another item. So state gets updated in dragOver.
And here is the code from the component. Note, the array of items being shown is stored in the parent component, see the updateState function:
Also, there is a shouldComponentUpdate so that you only get a re-render if the order will change.
Note: I'm not using a placeholder as it is not needed. As you drag the item to a new position, the whole list is updated on-the-fly.
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>
)
}
}