Search code examples
reactjsreact-hooksdrag-and-dropuse-statereact-dnd

React Drag-and-Drop with useState is overwriting previous state


I'm working on a page builder of sorts for my product, and I'm using react-dnd to accomplish what I need. So far I've gotten the basic drag-and-drop functionality taken care of, and I'm able to add a single item to my page, but I'm hitting a snag.

When I drag a new item to my drop zone, the expected behavior is that it will add a new item to the existing array that I've got stored in a nested object in state but, instead when I drag-and-drop another item to it it is overwriting the previous item, only allowing my to have a single item in my page. I have been beating my head against the wall all day. Below is my code; please comment if you need more and I will do my best to clarify.

So, here is my useState. I have a couple other items in here, but they're unimportant. All that matters is the content array:

const [lessonContent, setLessonContent] = useState({
    title: '',
    courseType: '',
    content: [],
});

My function to add an element to lessonContent.content which is passed to the useDrop hook:

const handleAddLessonElement = (element: Record<string, unknown>) => {
    setLessonContent({
      ...lessonContent,
      content: [...lessonContent.content, element],
    });
};

const [{ isOver }, drop] = useDrop(() => ({
    accept: 'card',
    drop: (item) => handleAddLessonElement(item),
    collect: (monitor) => ({
      isOver: !!monitor.isOver(),
    }),
}));

The useDrag hook:

const [{ isDragging }, drag] = useDrag(() => ({
    type: 'card',
    item: {
      type: element.value,
      value: '',
      config: { label: element.label },
    },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
}));

Also, I'm using the <DNDProvider> with HTML5Backend, and I've got it wrapping all components that are utilizing tools from react-dnd. As I said the basics are working, but I seem to be messing something up in adding to state. I just cannot, for the life of me, find what it is.

Thanks in advance!


Solution

  • Ended up figuring out, turns out it was a solution I had tried but hadn't implemented correctly.

    My problem is that I was not tracking the array as it updated, so I was constantly overwriting what I had just done. So, all it took was updating my handleAddLessonElement function to track the updated state before adding an element to the array.

    const handleAddLessonElement = (element: Record<string, unknown>) => {
       setLessonContent((previousState: Record<string, any>) => ({
         ...previousState,
         content: [...previousState.content, element],
       }));
    };
    

    As you can see, I added the previousState which will track the newly update state as I add elements, then use the spread operator to add these new elements to the copied array.