React使用Hooks操作嵌套对象时state不更新,react-beautiful-dnd拖拽无响应
问题排查与解决方案
1. 修复闭包陷阱导致的state取值错误
当前onDragEnd函数直接读取外部data变量,会因为React函数组件的闭包特性始终拿到旧的state值,导致每次更新都是基于初始数据修改,看起来好像没有触发状态更新。推荐改用setState的函数式更新形式,直接获取最新的state值:
import { useCallback } from 'react'; const onDragEnd = useCallback((result) => { const { source, destination } = result; // 拖拽到列表外直接返回 if (!destination) return; // 同分类拖拽逻辑 if (source.droppableId === destination.droppableId) { setData(prevData => { const catSkills = prevData.categories[source.droppableId].skillIds; const reorderedSkills = reorder(catSkills, source.index, destination.index); // 返回immutable更新后的新state return { ...prevData, categories: { ...prevData.categories, [source.droppableId]: { ...prevData.categories[source.droppableId], skillIds: reorderedSkills } } }; }); } }, []);
2. 修正Draggable组件的key取值
react-beautiful-dnd依赖Draggable的key属性识别元素位置,如果使用数组下标作为key,拖拽后数组顺序变化但下标不变,React会认为元素没有更新,导致UI不刷新。必须使用唯一固定值作为key:
// 错误写法:用index做key {skillIds.map((skillId, index) => ( <Draggable key={index} draggableId={skillId} index={index}> {/* 内容 */} </Draggable> ))} // 正确写法:用skillId做唯一key {skillIds.map((skillId, index) => ( <Draggable key={skillId} draggableId={skillId} index={index}> {/* 内容 */} </Draggable> ))}
3. 确保渲染逻辑与state同步
检查分类下技能列表的渲染逻辑,必须遍历当前分类的skillIds数组来取对应的技能数据,不要直接遍历初始的skills对象:
{data.categoryOrder.map(catId => { const category = data.categories[catId]; // 从当前state的skillIds取对应技能,确保和状态同步 const skillList = category.skillIds.map(skillId => data.skills[skillId]); return ( <Droppable droppableId={catId} key={catId}> {/* Droppable内容 */} </Droppable> ) })}
4. 检查react-beautiful-dnd内置props是否正确挂载
必须把库提供的props和ref挂载到对应节点,同时保留placeholder节点,否则拖拽逻辑会出现异常:
// Droppable 正确示例 <Droppable droppableId={catId}> {(provided) => ( <div ref={provided.innerRef} {...provided.droppableProps}> {/* 技能列表内容 */} {provided.placeholder} </div> )} </Droppable> // Draggable 正确示例 <Draggable key={skillId} draggableId={skillId} index={index}> {(provided) => ( <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} > {skill.name} </div> )} </Draggable>
按照以上步骤排查修改后,同分类拖拽的状态更新和UI渲染即可正常生效。
内容的提问来源于stack exchange,提问作者Jonny Burch
相关产品推荐
相关产品推荐

