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

React中react-beautiful-dnd拖拽失效:ButtonBeginningPages拖动后复位

问题原因

你的代码里Draggable组件的index是硬编码的固定值,且没有在拖拽结束后更新页面列表的数据源顺序。react-beautiful-dnd仅负责拖拽的UI交互,真正的列表顺序变更需要手动更新状态/数据源,否则组件会因数据源未变化,重新渲染时回到原始位置。

修复步骤

1. 定义可维护的起始页面数据源

把硬写的起始页面改成数组形式,作为组件状态(若需全局维护可从props传入):

// 在组件内部初始化状态
const [beginningPages, setBeginningPages] = React.useState([
  { id: 'frontCover', text: 'Front Cover', screenType: 'coverPage' },
  { id: 'titlePage', text: 'Title Page', screenType: 'titlePage' },
  { id: 'dedicationPage', text: 'Dedication', screenType: 'dedicationPage' },
  { id: 'introductionPage', text: 'Introduction', screenType: 'introductionPage' },
  { id: 'tocPage', text: 'Table of Contents', screenType: 'tocPage' },
]);

2. 添加拖拽结束的处理逻辑

给DragDropContext绑定onDragEnd函数,处理拖拽后的顺序更新:

const handleDragEnd = (result) => {
  // 拖拽取消时直接返回
  if (!result.destination) return;
  // 拖拽到原位置时无需更新
  if (result.source.index === result.destination.index) return;

  // 复制原数组,避免直接修改状态
  const newPages = Array.from(beginningPages);
  // 取出被拖拽项并插入到目标位置
  const [movedItem] = newPages.splice(result.source.index, 1);
  newPages.splice(result.destination.index, 0, movedItem);

  // 更新状态
  setBeginningPages(newPages);
};

修改DragDropContext的调用:

<DragDropContext onDragEnd={handleDragEnd}>

3. 动态渲染Draggable组件

将硬写的5个Draggable改为循环渲染,根据beginningPages数组的顺序生成:

<AccordionBox
  text="Beginning Pages"
  isOpen={beginningPagesIsOpen}
  toggle={toggleBeginningNav}
  rightCta={<div className={css.tooltip}><Tooltip /></div>}
>
  {beginningPages.map((page, index) => (
    <Draggable key={page.id} draggableId={page.id} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
        >
          <ButtonBeginningPages
            text={page.text}
            icon={
              page.screenType === 'coverPage' && coverPageIsUpdating ? <Spinner size="small" /> :
              page.screenType === 'titlePage' && titlePageIsUpdating ? <Spinner size="small" /> :
              page.screenType === 'dedicationPage' && dedicationPageIsUpdating ? <Spinner size="small" /> :
              page.screenType === 'introductionPage' && introductionPageIsUpdating ? <Spinner size="small" /> :
              null
            }
            active={
              page.screenType === 'introductionPage' 
                ? displayedScreen === 'introductionPage' || displayedScreen === 'introductionSubpage'
                : displayedScreen === page.screenType
            }
            canEditPage={!isContributor}
            onClick={() => { updateCurrentPage({ type: page.screenType }) }}
          />
        </div>
      )}
    </Draggable>
  ))}
</AccordionBox>

额外注意事项

  • 若页面顺序需要全局同步(比如和后端交互),需将beginningPages状态提升到父组件或使用状态管理工具,并在handleDragEnd中调用对应的更新接口。
  • 确保draggableId唯一且稳定,不能随索引变化,这里使用页面的id即可满足要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 08:50:07