使用React DnD实现列表拖拽排序 您所在的位置:网站首页 react拖动改变位置 使用React DnD实现列表拖拽排序

使用React DnD实现列表拖拽排序

#使用React DnD实现列表拖拽排序| 来源: 网络整理| 查看: 265

本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载

概述

项目中需要对列表实现拖拽排序,同时要支持点击选中和删除功能。

主要实现以下功能:

鼠标hover到【列表项】,显示可【拖动图标】;抓取【拖动图标】并拖动,【列表项】跟随鼠标;拖动过程【其他列表项】自行挪动;拖动到目标位置,释放鼠标,完成排序;

由于项目使用 React,因此用到 React DnD 来实现。

React DnD 是一组 React 高阶组件,使用的时候只需要将对应的 API 将目标组件进行包裹,即可实现拖动或接受拖动元素的功能。

可以在 codesandbox 查看 React DnD 例子的源码,包含ES6、ES7的实现。

实现详解实现列表

components/List.js

import React, { useState } from "react"; import { faTrashAlt, faArrowsAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classnames from "classnames"; function Item(props) { const { item, ...restProps } = props; return (

{item.title || "标题"}

); } function List(props) { let { list: propsList, activeItem } = props; propsList = propsList.map(item => { const isActive = activeItem.id === item.id; item = isActive ? activeItem : item; item.active = isActive; return item; }); const [list, setList] = useState(propsList); const find = id => { const item = list.find(c => `${c.id}` === id); return { item, index: list.indexOf(item) }; }; const onClick = event => { const { id } = event.currentTarget; const { item } = find(id); props.onClick(item); }; return ( {list.map((item, index) => ( {index + 1} ))} ); } export default List;

App.js

import React, { useState } from "react"; import ReactDOM from "react-dom"; import List from "./components/List"; import "./styles.scss"; const defaultList = [ { id: 1, title: "item1" }, { id: 2, title: "item2" }, { id: 3, title: "item3" }, { id: 4, title: "item4" }, { id: 5, title: "item5" } ]; function App() { const [list, setList] = useState(defaultList); const [activeItem, setActiveItem] = useState(list[0]); const onClick = item => { if (item.id !== activeItem.id) { setActiveItem(item); } }; return ; } ReactDOM.render(, document.getElementById("root"));

首先简单的实现一个列表,hover 列表项显示操作按钮,点击列表项可以选中。

安装 React DnD# Using npm npm i -s react-dnd react-dnd-html5-backend # Using yarn yarn add react-dnd react-dnd-html5-backend

这里 react-dnd-html5-backend 是使用 HTML5 的拖放API。也可以选择其他第三方库。

React DnD 核心 APIDragSource:用于包装需要拖动的组件,使组件能够被拖拽(make it draggable)。DropTarget:用于包装接收拖拽元素的组件,使组件能够放置(dropped on it)。DragDropContex:用于包装拖拽根组件,DragSource 和 DropTarget 都需要包裹在 DragDropContex 内。

详细用法请参考 React DnD 文档 或 react-dnd 用法详解

实现列表拖拽排序

components/DndList.js

import React, { useState } from "react"; import { DragSource, DropTarget, DragDropContext } from "react-dnd"; import HTML5Backend from "react-dnd-html5-backend"; import { faTrashAlt, faArrowsAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classnames from "classnames"; function Item(props) { const { // 这些 props 由 React DnD注入,参考`collect`函数定义 isDragging, connectDragSource, connectDragPreview, connectDropTarget, // 这些是组件收到的 props item, style = {}, find, move, change, remove, ...restProps } = props; const opacity = isDragging ? 0.5 : 1; const onRemove = event => { event.stopPropagation(); remove(item); } return connectDropTarget( // 列表项本身作为 Drop 对象 connectDragPreview( // 整个列表项作为跟随拖动的影像

{item.title || "任务标题"}

{ connectDragSource( ) // 拖动图标作为 Drag 对象 } ) ); } const type = "item"; const dragSpec = { // 拖动开始时,返回描述 source 数据。后续通过 monitor.getItem() 获得 beginDrag: props => ({ id: props.id, originalIndex: props.find(props.id).index }), // 拖动停止时,处理 source 数据 endDrag(props, monitor) { const { id: droppedId, originalIndex } = monitor.getItem(); const didDrop = monitor.didDrop(); // source 是否已经放置在 target if (!didDrop) { return props.move(droppedId, originalIndex); } return props.change(droppedId, originalIndex); } }; const dragCollect = (connect, monitor) => ({ connectDragSource: connect.dragSource(), // 用于包装需要拖动的组件 connectDragPreview: connect.dragPreview(), // 用于包装需要拖动跟随预览的组件 isDragging: monitor.isDragging() // 用于判断是否处于拖动状态 }); const dropSpec = { canDrop: () => false, // item 不处理 drop hover(props, monitor) { const { id: draggedId } = monitor.getItem(); const { id: overId } = props; // 如果 source item 与 target item 不同,则交换位置并重新排序 if (draggedId !== overId) { const { index: overIndex } = props.find(overId); props.move(draggedId, overIndex); } } }; const dropCollect = (connect, monitor) => ({ connectDropTarget: connect.dropTarget() // 用于包装需接收拖拽的组件 }); const DndItem = DropTarget(type, dropSpec, dropCollect)( DragSource(type, dragSpec, dragCollect)(Item) ); function List(props) { let { list: propsList, activeItem, connectDropTarget } = props; propsList = propsList.map(item => { const isActive = activeItem.id === item.id; item = isActive ? activeItem : item; item.active = isActive; return item; }); const [list, setList] = useState(propsList); const find = id => { const item = list.find(c => `${c.id}` === id); return { item, index: list.indexOf(item) }; }; const move = (id, toIndex) => { const { item, index } = find(id); list.splice(index, 1); list.splice(toIndex, 0, item); setList([...list]); }; const change = (id, fromIndex) => { const { index: toIndex } = find(id); props.onDropEnd(list, fromIndex, toIndex); }; const remove = item => { const newList = list.filter(it => it.id !== item.id); setList(newList); props.onDelete(newList); }; const onClick = event => { const { id } = event.currentTarget; const { item } = find(id); props.onClick(item); }; return connectDropTarget( {list.map((item, index) => ( {index + 1} ))} ); } const DndList = DropTarget(type, {}, connect => ({ connectDropTarget: connect.dropTarget() }))(List); // 将 HTMLBackend 作为参数传给 DragDropContext export default DragDropContext(HTML5Backend)(DndList);

App.js

import React, { useState } from "react"; import ReactDOM from "react-dom"; import List from "./components/DndList"; import "./styles.scss"; const defaultList = [ { id: 1, title: "item1" }, { id: 2, title: "item2" }, { id: 3, title: "item3" }, { id: 4, title: "item4" }, { id: 5, title: "item5" } ]; function App() { const [list, setList] = useState(defaultList); const [activeItem, setActiveItem] = useState(list[0]); const onDropEnd = (list, fromIndex, toIndex) => { setList([...list]); }; const onDelete = list => { setList([...list]); }; const onClick = item => { if (item.id !== activeItem.id) { setActiveItem(item); } }; return ( ); } ReactDOM.render(, document.getElementById("root"));

源码地址



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有