如何用React DnD的useDrop实现FullCalendar事件拖拽至垃圾桶删除?
实现FullCalendar事件拖拽至垃圾桶删除(React Hooks + React DnD)
可以通过React DnD的useDrop实现这个需求,下面是具体的改造步骤和代码:
1. 定义统一的拖拽类型常量
先定义一个拖拽类型标识,避免硬编码,确保拖拽源和放置目标匹配:
const EVENT_ITEM_TYPE = 'fullcalendar-event';
2. 完善useDrop配置
修改你的useDrop钩子,指定accept为上面定义的类型,并在drop回调中获取要删除的事件ID:
const [draggedEventId, setDraggedEventId] = useState(null); const [, dropTarget] = useDrop({ accept: EVENT_ITEM_TYPE, drop: (item, monitor) => { // 获取拖拽的事件ID,优先从monitor的item中取,其次用状态记录的值 const targetEventId = item?.eventId || draggedEventId; if (targetEventId) { dispatch({ type: "REMOVE_EVENT", payload: targetEventId }); } }, // 可选:拖拽悬停时给垃圾桶添加高亮样式 hover: () => { // 这里可以修改垃圾桶的样式,比如添加active类 } });
3. 处理FullCalendar的拖拽事件
在eventDragStart中记录当前拖拽的事件ID,并适配React DnD的HTML5后端传递数据:
const handleDragStart = (info) => { setDraggedEventId(info.event.id); // 给React DnD传递拖拽数据,确保放置目标能识别 if (info.jsEvent.dataTransfer) { info.jsEvent.dataTransfer.effectAllowed = 'move'; info.jsEvent.dataTransfer.setData( `application/react-dnd-${EVENT_ITEM_TYPE}`, JSON.stringify({ eventId: info.event.id }) ); } }; const handleDragEnd = () => { // 拖拽结束后清空状态 setDraggedEventId(null); };
4. 确保垃圾桶组件支持Ref
你的BinSection组件需要用forwardRef包装,才能接收useDrop返回的ref作为放置目标:
const BinSection = React.forwardRef((props, ref) => { return ( <div ref={ref} style={{ marginTop: '20px', padding: '12px', border: '2px dashed #e0e0e0', borderRadius: '4px', textAlign: 'center', color: '#666', cursor: 'move' }} > 🗑️ 拖拽事件到此处删除 </div> ); });
完整修改后的Schedule组件
import React, { useState, forwardRef } from 'react'; import { useDrop } from 'react-dnd'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import FunkFullCalendar from './FunkFullCalendar'; const EVENT_ITEM_TYPE = 'fullcalendar-event'; const BinSection = forwardRef((props, ref) => { return ( <div ref={ref} style={{ marginTop: '20px', padding: '12px', border: '2px dashed #e0e0e0', borderRadius: '4px', textAlign: 'center', color: '#666', cursor: 'move' }} > 🗑️ 拖拽事件到此处删除 </div> ); }); const Schedule = () => { const [draggedEventId, setDraggedEventId] = useState(null); // 保留你原有的状态:view, startDate, endDate, sessions等 const [, dropTarget] = useDrop({ accept: EVENT_ITEM_TYPE, drop: (item, monitor) => { const targetEventId = item?.eventId || draggedEventId; if (targetEventId) { dispatch({ type: "REMOVE_EVENT", payload: targetEventId }); } } }); const handleDragStart = (info) => { setDraggedEventId(info.event.id); if (info.jsEvent.dataTransfer) { info.jsEvent.dataTransfer.effectAllowed = 'move'; info.jsEvent.dataTransfer.setData( `application/react-dnd-${EVENT_ITEM_TYPE}`, JSON.stringify({ eventId: info.event.id }) ); } }; const handleDragEnd = () => { setDraggedEventId(null); }; return ( <DndProvider backend={HTML5Backend}> <FunkFullCalendar today={() => { let startDate = moment().tz(timeZone, true).startOf('week'); let endDate = moment().tz(timeZone, true).endOf('week'); changeInterval(startDate.toISOString(), endDate.toISOString()); }} timeGridWeek={() => { setView('timeGridWeek'); }} allDaySlot={false} defaultView='timeGridWeek' view={view} events={sessions} eventDragStart={handleDragStart} eventDragStop={handleDragEnd} dateClick={(evt) => { let correctedDate = moment(evt.date).utc().tz(timeZone, true); createSession(correctedDate); }} drop={(evt) => { let correctedDate = moment(evt.date).utc().tz(timeZone, true); createSession(correctedDate, evt.draggedEl.dataset.actorid); }} eventClick={(evt) => { openSession(evt.event.id); }} eventChange={(evt) => { let correctedStart = moment(evt.event.start).utc().tz(timeZone, true); let correctedEnd = moment(evt.event.end).utc().tz(timeZone, true); changeSessionTime(evt.event.id, correctedStart, correctedEnd); }} startDate={startDate} endDate={endDate} /> <BinSection ref={dropTarget}/> </DndProvider> ); }; export default Schedule;
核心要点
accept属性值:必须和拖拽源的类型完全一致,这里用EVENT_ITEM_TYPE统一标识FullCalendar事件,确保垃圾桶只接收目标拖拽元素。- 拖拽数据传递:通过
eventDragStart中的dataTransfer传递事件ID,或者用组件状态记录,保证drop回调能拿到要删除的事件ID。 - 垃圾桶Ref:必须用
forwardRef包装垃圾桶组件,否则无法接收useDrop返回的ref,无法成为有效的放置目标。
内容的提问来源于stack exchange,提问作者Zlatika
相关产品推荐
相关产品推荐

