You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 17:57:03