React待办列表拖拽后state未更新,如何实现拖拽完成后状态同步?
解决方案
核心思路
你当前直接操作DOM实现拖拽的方式和React状态驱动视图的运行逻辑冲突,需要将拖拽行为转化为对tasks状态的修改,由状态变更自动驱动视图更新,无需手动操作DOM修改节点位置。
步骤1:调整拖拽信息存储逻辑
不要存储拖拽的DOM节点,改为存储被拖拽任务的ID和所属的原始分组:
// 替换原来的draggableTodo变量 let draggedTask = null; const dragStart = (event) => { // 从任务节点的自定义属性读取信息 const { taskId, sourceGroup } = event.target.dataset; draggedTask = { id: Number(taskId), sourceGroup: sourceGroup }; // 可保留原有的隐藏样式逻辑,不影响状态更新 setTimeout(() => { event.target.style.display = "none"; }, 0); };
步骤2:修改drop事件处理逻辑,实现状态更新
const dragDrop = (event) => { event.preventDefault(); event.target.style.border = "none"; // 获取目标分组(板块div的id和tasks的分组key对应) const targetGroup = event.currentTarget.id; // 拖拽到原分组无需更新 if (draggedTask.sourceGroup === targetGroup) return; // 不可变更新state setTasks(prev => { // 浅拷贝所有分组 const newGroups = {...prev}; // 从原分组过滤掉被拖拽的任务 const sourceList = newGroups[draggedTask.sourceGroup].filter(item => item._id !== draggedTask.id); // 找到被拖拽的任务对象 const movedTask = newGroups[draggedTask.sourceGroup].find(item => item._id === draggedTask.id); // 把任务添加到目标分组 const targetList = [...newGroups[targetGroup], movedTask]; // 返回新的state return { ...newGroups, [draggedTask.sourceGroup]: sourceList, [targetGroup]: targetList }; }); // 删掉原来的appendChild逻辑,不需要手动操作DOM };
步骤3:修改JSX结构,添加必要的自定义属性
- 给每个板块div的id改为和tasks的分组key完全对应:
{/* Todo板块 */} <div id="todo" {/* 原来的id是todo_div,改成todo */} className="tasks-body_child" onDragOver={dragOver} onDragLeave={dragLeave} onDrop={dragDrop} > {/* In Progress板块 */} <div id="inProgress" {/* 原来的id是inProgress_div,改成inProgress */} className="tasks-body_child " onDragOver={dragOver} onDragEnter={dragEnter} onDragLeave={dragLeave} onDrop={dragDrop} > {/* Done板块 */} <div id="done" {/* 原来的id是done_div,改成done */} className="tasks-body_child" onDragOver={dragOver} onDragEnter={dragEnter} onDragLeave={dragLeave} onDrop={dragDrop} >
- 给每个任务节点添加自定义属性,存储任务ID和所属分组:
{/* Todo分组的任务 */} {tasks.todo.map((todo) => { return ( <div key={todo._id} data-task-id={todo._id} data-source-group="todo" className="tasks-bd_content" draggable={true} onDragStart={dragStart} onDragEnd={dragEnd} > {todo.name} </div> ); })} {/* In Progress分组的任务 */} {tasks.inProgress.map((inprogress) => { return ( <div key={inprogress._id} data-task-id={inprogress._id} data-source-group="inProgress" className="tasks-bd_content" draggable={true} onDragStart={dragStart} onDragEnd={dragEnd} > {inprogress.name} </div> ); })} {/* Done分组的任务 */} {tasks.done.map((done) => { return ( <div key={done._id} data-task-id={done._id} data-source-group="done" className="tasks-bd_content" draggable={true} onDragStart={dragStart} onDragEnd={dragEnd} > {done.name} </div> ); })}
补充说明
dragEnd事件里的样式恢复逻辑可以保留,也可以直接删除,因为状态更新后React会重新渲染所有任务节点,原来的DOM节点修改会被覆盖。如果发现拖拽结束后样式异常,可以直接去掉dragStart里修改display的逻辑,改用CSS的opacity属性控制拖拽时的视觉效果,避免和React渲染冲突。
内容的提问来源于stack exchange,提问作者Ajit Kumar
相关产品推荐
相关产品推荐

