如何在react-dnd中使用useDragLayer替换useDrag优化拖拽预览?
React DnD useDragLayer 实现自定义拖拽预览方案
第一步:调整原有useDrag逻辑
原来的useDrag只需要保留拖拽源的绑定,不需要再绑定默认预览连接器,同时禁用默认预览避免冲突:
// 原组件修改后的useDrag代码 const [{ isDragging }, connectDragSource] = useDrag({ item, collect: monitor => ({ isDragging: monitor.getItem()?.index === item.index, }), options: { dropEffect: 'move' } }) return ( <Wrapper isDragging={isDragging}> <DragHandle ref={connectDragSource} /> </Wrapper> )
第二步:实现自定义拖拽层组件
useDragLayer的核心作用是全局订阅拖拽状态,你可以在它的collect函数中获取所有你需要的拖拽上下文,自行控制预览的渲染逻辑,不需要任何连接器:
import { useDragLayer } from 'react-dnd' const CustomDragLayer = () => { // 从useDragLayer的collect参数中拿到全局拖拽状态 const { isDragging, draggedItem, clientOffset } = useDragLayer((monitor) => ({ isDragging: monitor.isDragging(), draggedItem: monitor.getItem(), clientOffset: monitor.getClientOffset(), })) // 非拖拽状态直接返回空 if (!isDragging || !clientOffset || !draggedItem) return null // 预览元素跟随鼠标位置的定位样式 const previewStyle = { position: 'fixed', top: clientOffset.y, left: clientOffset.x, zIndex: 9999, pointerEvents: 'none', // 避免预览层挡住鼠标事件 } // 自定义渲染你需要的预览内容,和原组件结构保持一致即可 return ( <div style={previewStyle}> <Wrapper isDragging={true}> {/* 根据拖拽项的数据渲染对应内容 */} 第{draggedItem.index}项的预览内容 </Wrapper> </div> ) }
第三步:挂载拖拽层到应用顶层
将CustomDragLayer组件放到你应用的最外层DOM节点下(比如App组件的根节点内部,和路由、全局弹窗同级),它会全局监听所有拖拽操作,自动渲染对应预览。
核心逻辑说明
- 你不需要将
useDragLayer和原有拖拽组件做任何关联,它会自动从react-dnd的全局上下文获取拖拽状态 - collect函数返回的所有值都会作为
useDragLayerHook的返回值,你直接拿这些值控制渲染逻辑即可,不需要连接器 - 这种方案完全接管了拖拽预览的渲染,不存在原生预览的各种兼容问题
内容的提问来源于stack exchange,提问作者Michal Kurz
相关产品推荐
相关产品推荐

