如何让基于dnd-kit的可拖拽Div同时支持编辑功能?
解决@dnd-kit/core中可拖拽组件内编辑按钮无法点击的问题
你的问题出在两个核心点:语法错误和拖拽事件与按钮点击的冲突,下面是具体修复方案:
问题1:无效语法导致状态更新失败
你在handleBlur函数里写了错误的语法:
const handleBlur = () `your text`=> { setIsEditing(false); };
多余的your text会导致函数无法正常执行,直接修正为标准箭头函数写法即可。
问题2:拖拽事件拦截了按钮点击
useSortable返回的listeners包含pointerdown监听,当你把{...listeners}绑定到整个容器div上时,这个监听会拦截所有子元素的点击事件,包括编辑按钮的点击。
解决方法是将拖拽交互限制在单独的手柄元素上,不让整个容器响应拖拽,这样按钮的点击事件就能正常触发。
修复后的完整代码
import React, { useState, useEffect } from 'react'; import { closestCenter, DndContext, PointerSensor, useSensor } from '@dnd-kit/core'; import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; const UserComponent = ({ id, body }) => { const [isEditing, setIsEditing] = useState(false); const toggleEditing = () => { setIsEditing(!isEditing); }; // 修正语法错误 const handleBlur = () => { setIsEditing(false); }; const { setNodeRef, attributes, listeners, transition, transform, isDragging, } = useSortable({ id: id }) const style = { transition, transform: CSS.Transform.toString(transform), border: '2px solid black', marginBottom: 5, marginTop: 5, display: "flex", alignItems: "center", gap: "10px", padding: "8px", opacity: isDragging ? 0.5 : 1, } return ( <div ref={setNodeRef} style={style} > {/* 仅给拖拽手柄绑定拖拽相关属性与监听 */} <div {...attributes} {...listeners} style={{cursor: "grab"}}>☰</div> <button onClick={toggleEditing}>Edit</button> <div contentEditable={isEditing} onBlur={handleBlur} suppressContentEditableWarning style={{flex: 1, textAlign: "left"}} > {body} </div> </div> ) } function DragApp() { const [items, setItems] = useState([ { id: "1", name: "Manoj", body: "Sample text 1" }, { id: "2", name: "John", body: "Sample text 2" }, { id: "3", name: "Ronaldo", body: "Sample text 3" }, { id: "4", name: "Harry", body: "Sample text 4" }, { id: "5", name: "Jamie", body: "Sample text 5" } ]) useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts') .then((response) => response.json()) .then((data) => setItems(data)); }, []); const sensors = [useSensor(PointerSensor)]; const handleDragEnd = ({active, over}) => { if (active.id !== over.id) { setItems((items) => { const oldIndex = items.findIndex(item => item.id === active.id) const newIndex = items.findIndex(item => item.id === over.id) return arrayMove(items, oldIndex, newIndex) }) } } return ( <div style={{ margin: 'auto', width: 1000, textAlign: 'center', }} > <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd} > <SortableContext items={items.map(item => item.id)} strategy={verticalListSortingStrategy} > { items.map( item => <UserComponent {...item} key={item.id} /> ) } </SortableContext> </DndContext> </div> ); } export default DragApp;
关键修复说明
- 修正
handleBlur语法错误,确保失去焦点时能正常关闭编辑状态。 - 将拖拽相关的
attributes和listeners从容器div移到单独的拖拽手柄上,只有点击手柄才触发拖拽,不会干扰编辑按钮的点击。 - 调整容器为flex布局,让拖拽手柄、按钮和文本区域的布局更合理。
内容的提问来源于stack exchange,提问作者Dan Gleason
相关产品推荐
相关产品推荐

