I'm implementing a drag-and-drop feature using React Beautiful DND for managing files and folders from a cloud storage via a Node.js backend. Each item (file or folder) is represented by a separate React component. However, I'm encountering an issue where dragged items are moving automatically during the drag operation, and I'm also receiving an error: "invariant failed: draggable[id: vacation.jpg]: requires an integer index prop."
Here's a simplified version of my React components:
// main content component which render all the medias into the screen
const Content = () => {
const navigate = useNavigate()
const location = useLocation()
const [isLoading, setIsLoading] = useState(true)
const handleFolderClick = (path) => {
navigate(`${location.pathname}/${path}`)
}
useEffect(() => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
setIsLoading(true)
}, [location.pathname])
if (isLoading)
return (
<div className="spinner-border" role="status">
<span className="visually-hidden">Loading...</span>
</div>
)
return (
<div className="container-fluid px-5 section-grid">
<DragDropContext
onDragEnd={(result) => {
console.log(result.destination)
if (!result.destination) return
}}
>
{contents.children.map((item, index) => {
if (item.type === 'folder') {
return (
<Folder
key={index}
name={item.name}
index={index}
onDirectoryNavigate={handleFolderClick}
/>
)
} else {
return <File key={index} name={item.name} index={index} />
}
})}
</DragDropContext>
<Outlet />
</div>
)
}
export default Content
// folder component
function Folder({ name, index, onDirectoryNavigate }) {
return (
<Droppable droppableId={name}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
style={{ transform: snapshot.isDraggingOver && 'none' }}
>
<Draggable draggableId={name} index={index} key={index}>
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.draggableProps}>
<div
className="position-relative"
style={{
filter: 'drop-shadow(10px 10px 15px #11111143)',
userSelect: 'none',
transform: snapshot.isDragging ? 'scale(.7)' : 'scale(1)',
transition: 'all .2s ease-in-out',
}}
onDoubleClick={() => onDirectoryNavigate(name)}
>
<div className="position-absolute w-100 h-100 p-5 d-flex align-items-center justify-content-center">
<button
style={{ all: 'unset', top: 7, left: 10, cursor: 'move' }}
className="position-absolute text-primary"
{...provided.dragHandleProps}
>
<FontAwesomeIcon icon={faUpDownLeftRight} />
</button>
<button
style={{ all: 'unset', top: 40, right: 10, fontSize: 22 }}
className="position-absolute text-primary"
>
<FontAwesomeIcon icon={faEllipsisVertical} />
</button>
<span className="fw-bold text-center text-primary">
{name}
</span>
</div>
<svg
className="w-100 h-100"
width="255"
height="179"
viewBox="0 0 255 179"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M255 158.224C255 166.502 254.282 170.293 249.5 174.002C244.718 177.711 242 178.002 236 178.002H25.5C9.5 178.002 10.2822 177.711 5.5 174.002C0.717823 170.293 0 164.748 0 159.502V18.5022C0 13.2568 0.717823 7.21125 5.5 3.5022C10.2822 -0.206857 14 0.00214401 25.5 0.00214401H67L92.5 29.6688H229.5C236.263 29.6688 242.749 31.7525 247.531 35.4616C252.313 39.1706 255 44.2012 255 49.4466V158.224Z"
fill="white"
/>
</svg>
</div>
</div>
)}
</Draggable>
{provided.placeholder}
</div>
)}
</Droppable>
)
}
// file component
function File({ name, index }) {
return (
<Droppable droppableId={name}>
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
<Draggable draggableId={name} index={index} key={index}>
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.draggableProps}>
<div
className="position-relative"
style={{
filter: 'drop-shadow(10px 10px 15px #11111143)',
userSelect: 'none',
transform: snapshot.isDragging ? 'scale(.6)' : 'scale(1)',
transition: 'all .2s ease-in-out',
}}
>
<div className="position-absolute w-100 h-100 p-5 d-flex align-items-center justify-content-center">
<button
style={{ all: 'unset', top: 7, left: 10, cursor: 'move' }}
className="position-absolute text-primary"
{...provided.dragHandleProps}
>
<FontAwesomeIcon icon={faUpDownLeftRight} />
</button>
<button
style={{ all: 'unset', top: 5, right: 10, fontSize: 22 }}
className="position-absolute text-primary"
>
<FontAwesomeIcon icon={faEllipsisVertical} />
</button>
<div className="d-flex flex-column align-items-center justify-content-center">
<div className="text-primary" style={{ fontSize: 42 }}>
<FontAwesomeIcon icon={faFileLines} />
</div>
<Link to={'/' + name} className="text-decoration-none">
<span className="fw-bold text-center text-primary">
{name}
</span>
</Link>
</div>
<button
style={{
all: 'unset',
position: 'absolute',
bottom: 5,
right: 15,
cursor: 'pointer',
}}
className="text-primary"
>
<FontAwesomeIcon icon={faCloudArrowDown} />
</button>
</div>
<svg
className="w-100 h-100"
width="255"
height="178"
viewBox="0 0 255 178"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="255" height="178" rx="16" fill="#FBFBFC" />
</svg>
</div>
</div>
)}
</Draggable>
{provided.placeholder}
</div>
)}
</Droppable>
)
}
I've noticed that when I remove the index prop from the Draggable component, the automatic movement issue is resolved, but then I encounter the "index prop" error. The issue appears to be related to how I'm handling the index prop and the drag-and-drop operation.
I have tried checking the data structure, using stable IDs, updating the order on drag end, and ensuring unique droppable IDs, but the issue persists. How can I properly handle the index prop and drag-and-drop operation to resolve these issues?
Any insights or suggestions would be greatly appreciated. Thank you!