React-Beautiful-Dnd doesn't work after component rerenders

357 Views Asked by At

I'm using React-Beautiful-Dnd and Im trying to drag and drop some items in the list, expected behavior that these items will change the look when the user starts dragging any item to look more compact and show more items in the screen, so I created a reusable component for the compact look and I have the regular look component, I have tried implementing this code below :

<Droppable droppableId={`${groupName}`}>
      {(provided) => (
        <div ref={provided.innerRef} {...provided.droppableProps} className= 
       {styles.items}>
          {groupedCollections[groupName].map((item, index) => (
        <Draggable
          key={item.items[0]?.id}
          draggableId={item.items[0]?.id}
          index={index}
        >
          {(provided, snapshot) => {
            return (
              <div
                ref={provided.innerRef}
                {...provided.draggableProps}
                {...provided.dragHandleProps}
              >
                {dragged ? (
                  <div>
                    <CollapsedItem item={item.items[0]} />
                  </div>
                ) : (
                  <div onDrag={() => setDragged(true)}>
                    <RegularLocation
                      collection={item}
                      onAddItems={() => handleAddItems(collection)}
                    />
                  </div>
                )}
              </div>
            );
          }}
        </Draggable>
      ))}
      {provided.placeholder}
    </div>
  )}
</Droppable>

and below is the drag context:

<DragDropContext onDragEnd={onDragEnd} onDragStart={() => setDragged(true)}>
    {returnItems()}
  </DragDropContext>

as you can see setDragged is global state, once it's true it should switch from RegularLocation to CollapsedItem, but problem is once it switches components the drag and drop doesn't work properly, some items are droppable some are not, some you can drop the draggable item in between some are not.

However if I use a single component with the same look it will work just fine, like if I use only CollapsedItem and not switch the component or use RegularLocation and not switch it will work just fine, what could be the reason and how can I fix it ?

2

There are 2 best solutions below

0
Ahmed Imam On

The issue you're facing seems to stem from the way you're handling the drag state and swapping out components in the middle of the drag operation. When you replace components during a drag operation, React's reconciliation process might unmount and remount the components, which can mess with react-beautiful-dnd's internal drag state and the native HTML5 drag-and-drop behavior.

Here's a recommended approach to solve the problem:

  1. Maintain a Stable DOM Structure: When you change from RegularLocation to CollapsedItem, you're completely swapping out one component for another. Instead of swapping them, always render both components but use styles to show/hide or adjust the appearance.

  2. Use CSS for the Compact Look: Instead of replacing components, adjust the appearance with CSS when dragging starts. This ensures the underlying DOM structure remains consistent during the drag operation.

  3. Using snapshot.isDragging: You can use the snapshot.isDragging property provided by the Draggable render prop function to determine if the current item is being dragged. This can help in applying styles.

Modified version of your code that uses CSS to adjust the appearance

<Droppable droppableId={`${groupName}`}>
  {(provided) => (
    <div ref={provided.innerRef} {...provided.droppableProps} className={styles.items}>
      {groupedCollections[groupName].map((item, index) => (
        <Draggable key={item.items[0]?.id} draggableId={item.items[0]?.id} index={index}>
          {(provided, snapshot) => (
            <div
              ref={provided.innerRef}
              {...provided.draggableProps}
              {...provided.dragHandleProps}
              className={snapshot.isDragging ? styles.dragging : ''}
            >
              <div className={dragged ? styles.collapsed : ''}>
                <RegularLocation
                  collection={item}
                  onAddItems={() => handleAddItems(collection)}
                />
              </div>
              {/* If you still want to use CollapsedItem */}
              <div className={!dragged ? styles.collapsed : ''}>
                <CollapsedItem item={item.items[0]} />
              </div>
            </div>
          )}
        </Draggable>
      ))}
      {provided.placeholder}
    </div>
  )}
</Droppable>
1
TKHAN VIN NHUIEN On

I encountered a similar problem in my TypeScript drag-and-drop project. After a bit of research, I discovered that React Strict Mode was causing compatibility issues with the library I was using. I resolved the issue by removing React Strict Mode from my app's root file. If you're facing similar problems, you can try the same by removing React Strict Mode from your application's entry point.