In my project I am trying to change my information into react-dnd beutiful drag and drop.
But as i changed it, the drag and drop does not work for new items inserted into the array.
How do I make it so that it works still in this example?
code sandbox: https://codesandbox.io/s/react-drag-and-drop-react-beautiful-dnd-forked-oo8nd?file=/src/index.js:0-5732
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
// fake data generator
const getItems = (count, offset = 0) =>
Array.from({ length: count }, (v, k) => k).map((k) => ({
id: `item-${k + offset}-${new Date().getTime()}`,
content: `item ${k + offset}`
}));
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
/**
* Moves an item from one list to another list.
*/
const move = (source, destination, droppableSource, droppableDestination) => {
const sourceClone = Array.from(source);
const destClone = Array.from(destination);
const [removed] = sourceClone.splice(droppableSource.index, 1);
destClone.splice(droppableDestination.index, 0, removed);
const result = {};
result[droppableSource.droppableId] = sourceClone;
result[droppableDestination.droppableId] = destClone;
return result;
};
const grid = 8;
const getItemStyle = (isDragging, draggableStyle) => ({
// some basic styles to make the items look a bit nicer
userSelect: "none",
padding: grid * 2,
margin: `0 0 ${grid}px 0`,
// change background colour if dragging
background: isDragging ? "lightgreen" : "grey",
// styles we need to apply on draggables
...draggableStyle
});
const getListStyle = (isDraggingOver) => ({
background: isDraggingOver ? "lightblue" : "lightgrey",
padding: grid,
width: 250
});
function QuoteApp() {
const [state, setState] = useState([getItems(10), getItems(5, 10)]);
function onDragEnd(result) {
const { source, destination } = result;
// dropped outside the list
if (!destination) {
return;
}
const sInd = +source.droppableId;
const dInd = +destination.droppableId;
if (sInd === dInd) {
const items = reorder(state[sInd], source.index, destination.index);
const newState = [...state];
newState[sInd] = items;
setState(newState);
} else {
const result = move(state[sInd], state[dInd], source, destination);
const newState = [...state];
newState[sInd] = result[sInd];
newState[dInd] = result[dInd];
setState(newState.filter((group) => group.length));
}
}
return (
<div>
<button
type="button"
onClick={() => {
setState([...state, []]);
}}
>
Add new group
</button>
<button
type="button"
onClick={() => {
setState([...state, getItems(1)]);
}}
>
Add new item
</button>
<div style={{ display: "flex" }}>
<DragDropContext onDragEnd={onDragEnd}>
{state.map((el, ind) => (
<Droppable key={ind} droppableId={`${ind}`}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={getListStyle(snapshot.isDraggingOver)}
{...provided.droppableProps}
>
{el.map((item, index) => (
<Draggable
key={item.id}
draggableId={item.id}
index={index}
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
<div
style={{
display: "flex",
justifyContent: "space-around"
}}
>
ind:{ind} , index{index} ,{item.content}
<button
type="button"
onClick={() => {
const newState = [...state];
newState[ind].splice(index, 1);
setState(
newState.filter((group) => group.length)
);
}}
>
delete
</button>
<button
type="button"
onClick={() => {
const newState = [...state];
newState[ind].splice(
index,
0,
getItems(1, index)
);
setState(
newState.filter((group) => group.length)
);
}}
>
add s/o
</button>
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
))}
</DragDropContext>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<QuoteApp />, rootElement);
The specific change is the "add s/o" button
Thankyou for your time
When you insert/add a "s/o" (sorry, not sure what that is) you splice in the return value from getItems
which is an array, but your main state shape is an array of objects.
const getItems = (count, offset = 0) =>
Array.from({ length: count }, (v, k) => k).map((k) => ({
id: `item-${k + offset}-${new Date().getTime()}`,
content: `item ${k + offset}`
}));
...
<button
type="button"
onClick={() => {
const newState = [...state];
newState[ind].splice(
index,
0,
getItems(1, index) // <-- inserts array!!
);
setState(
newState.filter((group) => group.length)
);
}}
>
add s/o
</button>
You've inserted an array instead of the object.
{newState: Array(2)}
newState: Array(2)
0: Array(11)
0: Array(1) // <-- inserted array
0: Object // <-- object you want inserted
id: "item-0-1609711045911"
content: "item 0"
1: Object
id: "item-0-1609711042837"
content: "item 0"
2: Object
3: Object
4: Object
5: Object
6: Object
7: Object
8: Object
9: Object
10: Object
1: Array(5)
Seems a rather simple solution is to pop the single inserted object from getItems
when splicing in the new "s/o".
<button
type="button"
onClick={() => {
const newState = [...state];
newState[ind].splice(
index,
0,
getItems(1, index).pop(), // <-- pop value from array
);
setState(
newState.filter((group) => group.length)
);
}}
>
add s/o
</button>