TypeScript重构可拖拽滚动MUI Tabs遇react-beautiful-dnd报错求助
修复React Beautiful Dnd + MUI Tabs的"无法找到draggable"错误
错误原因
- 用数组索引作为
draggableId和React元素key,拖拽后数组顺序改变,索引对应的元素发生变化,导致react-beautiful-dnd无法匹配到目标draggable元素。 - MUI的Tabs组件对直接子元素的结构有依赖,嵌套额外的div会干扰组件内部的DOM引用,引发识别问题。
- 克隆Tab元素时传递了冗余属性,导致事件处理逻辑冲突。
修复后的完整代码
import React from 'react'; import TabContext from "@mui/lab/TabContext"; import TabPanel from "@mui/lab/TabPanel"; import { DragDropContext, Droppable, DropResult, Draggable } from "react-beautiful-dnd"; import { Stack, styled, Tab, Tabs } from "@mui/material"; interface DraggableTabProps { label: string; value: string; index: number; draggableId: string; onChange: (event: React.SyntheticEvent, newValue: string) => void; currentValue: string; }; const DraggableTab = (props: DraggableTabProps) => { return ( <Draggable draggableId={props.draggableId} index={props.index} disableInteractiveElementBlocking > {(draggableProvided) => ( <Tab ref={draggableProvided.innerRef} {...draggableProvided.draggableProps} {...draggableProvided.dragHandleProps} label={props.label} value={props.value} onChange={props.onChange} selected={props.currentValue === props.value} /> )} </Draggable> ); } const StyledTabs = styled(Tabs)(); export default function DraggableTabComponent() { const [value, setValue] = React.useState('1'); const handleChange = (event: React.SyntheticEvent, newValue: string) => { setValue(newValue); }; const [tabs, setTabs] = React.useState( [...Array(25)].map((_, index) => ({ id: `tab${index + 1}`, label: `Tab ${index + 1}`, value: `${index + 1}`, content: `Content ${index + 1}` })) ); const onDragEnd = (result: DropResult) => { if (!result.destination) return; const newTabs = Array.from(tabs); const draggedTab = newTabs.splice(result.source.index, 1)[0]; newTabs.splice(result.destination.index, 0, draggedTab); setTabs(newTabs); // 拖拽后保持选中当前标签的内容 setValue(draggedTab.value); }; return ( <TabContext value={value}> <Stack> <DragDropContext onDragEnd={onDragEnd}> <Droppable droppableId="tabs" direction="horizontal"> {(droppableProvided) => ( <StyledTabs ref={droppableProvided.innerRef} {...droppableProvided.droppableProps} value={value} variant="scrollable" scrollButtons allowScrollButtonsMobile sx={{ minHeight: 48 }} // 保证拖拽区域高度稳定 > {tabs.map((tab, index) => ( <DraggableTab key={tab.id} draggableId={tab.id} label={tab.label} value={tab.value} index={index} onChange={handleChange} currentValue={value} /> ))} {droppableProvided.placeholder} </StyledTabs> )} </Droppable> </DragDropContext> </Stack> {tabs.map((tab) => ( <TabPanel value={tab.value} key={tab.id}> {tab.content} </TabPanel> ))} </TabContext> ); }
关键修复点
- 使用稳定的唯一标识:用每个tab的
id作为draggableId和React元素的key,拖拽后元素的标识不会随索引变化而改变,避免react-beautiful-dnd找不到目标元素。 - 简化Draggable结构:直接在Draggable内部渲染Tab组件,不再克隆元素,避免属性冲突和DOM结构干扰。
- 完善拖拽结束逻辑:增加
!result.destination的判断,处理拖拽未完成的情况;拖拽后自动选中被拖拽的标签,保持内容同步。 - 稳定Tabs高度:添加
sx={{ minHeight: 48 }}保证Tabs区域高度稳定,避免拖拽时布局跳动影响识别。
内容的提问来源于stack exchange,提问作者Hank
相关产品推荐
相关产品推荐

