Search code examples
arraysreactjsdrag-and-drop

using react-dnd beautiful how do I insert new item into the array, or does it not work?


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


Solution

  • Issue

    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)
    

    Solution

    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>
    

    Edit using-react-dnd-beautiful-how-do-i-insert-new-item-into-the-array-or-does-it-no