You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Beautiful DnD嵌套列表无需跨父级拖拽 子项无法连续拖拽问题

我当前正在尝试用React Beautiful DnD实现可拖拽的容器对象列表,在这些容器内部,我又构建了一个由可拖拽元素组成的子列表。我知道这个库官方技术上不支持嵌套列表,但我已经见过很多绕过方案,而且我的使用场景比绝大多数场景都更简单,因为我完全不需要把子列表项拖到不同的父列表项之间。

目前我已经封装了一个DnDList模块,可以在两个拖拽列表中复用代码。UI效果如下,两个列表都已圈出(敏感变量名已做涂黑处理):

图片(暂不可用,审核通过后我会补充上传)

红色圈出的数字对应父列表,运行完全符合预期,我可以顺利拖拽重排这些大的div容器,没有任何问题。但蓝色圈出的子列表存在异常。

这个子列表由简单的<p>元素组成,实际可以拖拽重排,但存在一个很奇怪的问题:我无法连续拖拽这些子项。比如我点击AlphaCase这个项可以正常拖拽一次,但一旦松手放下之后,我必须先点击一次父列表内的任意区域,才能再次拖拽子列表项。每次拖拽完嵌套列表元素之后的第一次点击,都会误触发外层父元素的拖拽逻辑,变成对父列表重排。但只要我点过一次父列表内的任意内容,就可以再次正常拖拽重排子列表组件,每次嵌套列表拖拽完都会重复这个流程。(很奇怪的是,点击父列表外的区域比如Start按钮不会生效,看起来这个“重置点击”的逻辑和焦点状态有关?)

以下是我的DnDList组件代码:

<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>

以下是组件的使用示例:

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

getAttributeKey函数仅返回唯一字符串标识符,作为每个Draggable对象的key使用。

父列表和子列表的getAttributeKey、handleOnDragEnd逻辑基本一致:

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;
};

(我已移除所有敏感变量名,用...替代)

再次说明,我甚至不需要把子列表元素拖到父列表的其他容器里,只需要保证可以在对应的子列表内部完成拖拽即可。


内容的提问来源于stack exchange,提问作者tprebenda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 01:18:04