React实现列表拖拽排序
本文主要介绍一下React实现列表拖拽排序方法,具体样式如下图
首先,简单展示一下组件的数据结构
const CodeSetting = props => {const {$t, // 国际化翻译函数vm, // 视图模型数据vm: {CodeSet: { Enable = [], // 启用的编码列表Disable = [] // 停用的编码列表}},getConfig, // 获取配置的函数save, // 保存配置的函数vmChange // 更新视图模型的函数} = props;
};
完整的数据结构示例
const vm = {CodeSet: {Enable: [{ Compression: "H.264" },{ Compression: "H.265" },{ Compression: "MPEG-4" }],Disable: [{ Compression: "AVC" },{ Compression: "HEVC" }]}
};
主要用到的代码如下,简单看后我将介绍拖拽方法
{Enable.length ? (<Cardtitle={`${$t('com.EnableCode')} (${Enable.length})`}extra={<Buttonsize='small'className='clear-all-btn'type='link'onClick={clearAllEnabled}>{$t('com.ClearAll')}</Button>}>{Enable.map((item, index) => (<divkey={index}className='drag-item'draggableonDragStart={e => {handleDragStart(e, index);}}onDragEnd={handleDragEnd}onDragOver={handleDragOver}onDrop={e => handleDrop(e, index)}><div className='drag-handle'>⋮⋮</div><LabelText text={item.Compression} /><div className='delete-btn-container'><Iconcomponent={remove}onClick={() => codeSetChange('remove', index)}style={{fontSize: '20px'}}/></div></div>))}</Card>
首先用到的组件是Card组件,title是card标题,extra是card后缀
之后遍历Enable数组,将拿到的每一个值渲染到card中
这个组件实现了 HTML5 原生拖拽 API 来实现编码列表的拖拽排序功能。主要使用了以下拖拽事件:
onDragStart - 拖拽开始
onDragOver - 拖拽悬停
onDrop - 拖拽放置
onDragEnd - 拖拽结束
状态管理
const [draggedIndex, setDraggedIndex] = useState(null); // 记录当前拖拽项的索引
拖拽事件处理函数
1 拖拽开始 (handleDragStart)
const handleDragStart = (e, index) => {setDraggedIndex(index); // 记录拖拽项的索引e.dataTransfer.effectAllowed = 'move'; // 设置拖拽效果为移动e.currentTarget.classList.add('dragging'); // 添加拖拽样式
};
2 拖拽悬停 (handleDragOver)
const handleDragOver = e => {e.preventDefault(); // 阻止默认行为e.dataTransfer.dropEffect = 'move'; // 设置放置效果为移动// 清除所有拖拽项的悬停样式const dragItems = document.querySelectorAll('.drag-item');dragItems.forEach(item => {item.classList.remove('drag-over');});// 为当前悬停元素添加悬停样式e.currentTarget.classList.add('drag-over');
};
3 拖拽放置 (handleDrop)
const handleDrop = (e, dropIndex) => {e.preventDefault();e.currentTarget.classList.remove('drag-over');if (draggedIndex === null || draggedIndex === dropIndex) {return;}// 重新排序 Enable 数组const enableList = [...Enable];const draggedItem = enableList[draggedIndex];// 移除拖拽项enableList.splice(draggedIndex, 1);// 在目标位置插入enableList.splice(dropIndex, 0, draggedItem);// 更新vm数据const newCodeSet = {...vm.CodeSet,Enable: enableList};vmChange({ CodeSet: newCodeSet });setDraggedIndex(null);
};
4 拖拽结束 (handleDragEnd)
const handleDragEnd = e => {setDraggedIndex(null);e.currentTarget.classList.remove('dragging');// 清除所有拖拽项的悬停样式const dragItems = document.querySelectorAll('.drag-item');dragItems.forEach(item => {item.classList.remove('drag-over');});
};
5.JSX 结构
<divkey={index}className='drag-item'draggable // 设置为可拖拽onDragStart={e => handleDragStart(e, index)} // 拖拽开始onDragEnd={handleDragEnd} // 拖拽结束onDragOver={handleDragOver} // 拖拽悬停onDrop={e => handleDrop(e, index)}> // 拖拽放置<div className='drag-handle'>⋮⋮</div> // 拖拽手柄<LabelText text={item.Compression} /><div className='delete-btn-container'>{/* 删除按钮 */}</div>
</div>
6. 核心算法
- 拖拽排序的核心算法是数组重排序:
- 获取拖拽项:从原位置取出拖拽的元素
- 移除拖拽项:在原位置删除该元素
- 插入新位置:在目标位置插入该元素
- 更新状态:将新的数组顺序更新到组件状态
7. 样式处理
组件通过 CSS 类名来管理拖拽状态:
.dragging - 拖拽中的样式
.drag-over - 拖拽悬停的样式
.drag-item - 可拖拽项的基础样式
8.样式代码
// 拖拽项容器.drag-item {display: flex;align-items: center;cursor: grab;&:hover {background-color: #f5f5f5;}// 拖拽中状态&.dragging {background-color: #e6f7ff;opacity: 0.5;transform: scale(0.95);}// 拖拽悬停状态&.drag-over {background-color: #e6f7ff;border: 1px solid #91d5ff;border-radius: 4px;}}// 拖拽手柄.drag-handle {margin-right: 8px;color: #2f2e2e;font-size: 12px;user-select: none;}