3

I am currently trying to use React Beautiful DnD to create a draggable list of container objects. Within these objects, I have also built a sub-list of draggable elements. I know the library technically doesn't support nested lists, but I have seen many work-arounds and I believe my use-case is even simpler than most, since I don't need to drag sublist items between parent list items.

As of now, I have created a DnDList module so that I can re-use the code for both draggable lists. Here is what the UI looks like, with both lists circled (and variable names blacked-out):

image (not available yet, I will share when its cleared)

The numbers circled in red are part of the parent list, which are working as expected. I can easily drag and re-order these bigger div containers without issue. The list circled in blue, however, is causing problems.

This sub-list is made up of simple <p> elements, and I actually am able to drag and re-order them, but there is a weird issue where I cannot continuously drag these items. I can click on an item (i.e. AlphaCase) and drag it once, but once it has been dropped, I must click anywhere within the parent list before I can drag it again. On first click AFTER dragging a nested list element, it incorrectly tries to drag the surrounding parent element and re-order the parent list. Once I have clicked on anything within the parent list, however, I can again drag and re-order the sub-list components. This process repeats after each nested list drag. (Strangely, clicking outside of the parent list, i.e. the Start button, does not count. Perhaps this 'reset click' has something to do with focus?)

Here is the code for my DnDList component:

<DragDropContext onDragEnd={handleDragEnd}>
  <Droppable droppableId={droppableId}>
    {(provided) => (
      <ol
        ref={provided.innerRef}
        {...provided.droppableProps}
        className={classes.root}
      >
        {React.Children.map(
          children,
          (child: React.ReactElement | undefined, index: number) => {
            if (child) {
              const keyVal: string = getAttributeKey(child.props);
              return (
                <Draggable key={keyVal} draggableId={keyVal} index={index}>
                  {(provided, snapshot) => (
                    <li
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                    >
                      {child}
                    </li>
                  )}
                </Draggable>
              );
            }
          }
        )}
        {provided.placeholder}
      </ol>
    )}
  </Droppable>
</DragDropContext>

And here is what it looks like in use:

<DnDList
  handleDragEnd={handleOnDragEnd}
  droppableId="experiments"
  getAttributeKey={getAttributeKey}
>
  {<array>.map(
    (experiment: string, index: number) => (
      <p key={index} className={classes.experiment}>
        {experiment}
      </p>
    )
  )}
</DnDList>

The getAttributeKey function just returns a unique identifier (string) to use as a key for each Draggable object.

For both the parent and the sub-list elements, the getAttributeKey and handleOnDragEnd functions are practically the same:

const handleOnDragEnd = (result: DropResult) => {
  if (!result.destination) {
    return;
  }
  const newOrder: string[] = Array.from(...);
  const [reorderedItem] = newOrder.splice(result.source.index, 1);
  newOrder.splice(result.destination.index, 0, reorderedItem);

  onChange(...);
};

const getAttributeKey = (props: any) => {
  return props.children;
};

(I removed any sensitive variable names and replaced them with '...')

Again, I don't even need to drag the sub-list elements into another element of the parent list, I only need to be able to drag them within that specific sub-list.

tprebenda
  • 389
  • 1
  • 6
  • 17

0 Answers0