react dnd 简单的拖拽排序案例

2,213 阅读2分钟

demo效果

1652535030703.gif 此demo主要是做一个拖拽排序功能,运用的场景比如轮播图展示的顺序等。 实现的大体思路是这样的:

  1. 先将可拖拽的区域最外层用 DndProvider组件并添加属性backend={HTML5Backend} 包裹,提供react dnd功能
  2. 然后定义拖拽 (useDrag) 和放置区 (useDrop) 的属性和方法,通过 drop(drag(ref)) 来给拖拽组件设置成可放置可拖拽组件
  3. 通过拖拽组件end方法获取当前拖拽组件的item对象的Index值和放置区drop函数返回的结果Index值,回调endFunc函数执行数组排序逻辑处理并更新页面

详细过程看一下代码,此代码结合了react框架 dva 进行编写;

import React, { useRef } from 'react'
import { connect } from 'dva'
import { DndProvider, useDrop, useDrag } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'


function Box({ name, obj, idx, endFunc }) {
    const ref = useRef(null);
    //先定义一个推拽组件 
    const [{ isDragging, handlerId }, drag] = useDrag(() => ({
        // { isDragging }一个包含从 collect 函数收集的属性的对象。如果没有collect定义函数,则返回一个空对象。
        type: "BOX",//作用:只有为相同类型注册的放置目标才会对此项目做出反应
        item: { index: idx },//必须要有的,相当于id
        end: (item, monitor) => {//end 在被拖拽的组件被放下后执行
            // monitor.getDropResult()返回表示最后记录的放置drop result对象(拖拽元素放下时的结果)
            const dropResult = monitor.getDropResult();//如果dropResult为null,代表拖拽组件没有放到指定区域,有则返回该区域的
            console.log(item, dropResult);
            if (item && dropResult) {//如果拖拽组件结束位置在规定区域则alert
                endFunc(item, dropResult, obj);
            }
        },
        //收集器:负责监听收集拖拽组件的状态
        collect: (monitor) => ({
            isDragging: monitor.isDragging(),//是否拖拽状态
        }),
    }));
    const [{ isOver }, drop] = useDrop(() => ({
        //accept:只接受类型为BOX的拖拽组件,否则不能感应
        accept: "BOX",
        drop: () => ({ index: obj.Index }),//放置区组件的结果(数据信息),此数据信息会被拖拽组件end方法的monitor.getDropResult()获取
    }));
    return (<div ref={drop(drag(ref))} role="Box" style={{ height: 200, width: 400, backgroundColor: obj.color, opacity: 1, fontSize: 40, textAlign: 'center' }} >
        {name}
    </div>);
};


const Container = (props) => {
    return (
        props.state.data.map((item, index) => <Box key={index} name={item.name} obj={item} endFunc={props.endFunc} idx={index} />)
    )
}
function NewDndController({ dispatch, dnd: state }) {
    // 当拖拽对象放置后触发,进行元素排序
    function endFunc(before, last) {
        let beforeIndex = before.index, lastIndex = last.index;
        let data = JSON.parse(JSON.stringify(state.data));
        state.data[beforeIndex] = data[lastIndex];
        state.data[beforeIndex].Index = beforeIndex;
        state.data[lastIndex] = data[beforeIndex];
        state.data[lastIndex].Index = lastIndex;
        dispatch({ type: 'dnd/save', payload: { ...state } })
    }
    return (
        <div style={{ width: '100vw', height: '100vh', padding: 50 }}>
            <DndProvider backend={HTML5Backend}>
                <div style={{ display: "flex", flexWrap: "wrap", justifyContent: "space-around", border: "1px solid #BDBBBB", padding: 20, }}>
                    <Container state={state} endFunc={endFunc} />
                </div>
            </DndProvider>
        </div >
    )
}
function mapStateToProps(state) {
    return { dnd: state.dnd }
}
export default connect(mapStateToProps)(NewDndController)