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

React中用dnd-kit结合useState与Redux保存拖拽排序位置

问题诊断与修复方案

拆分组件并接入Redux后拖拽排序、保存位置及删除功能失效,核心问题大概率出在状态同步逻辑、Redux状态不可变性或组件通信传递上,以下是针对性修复步骤:

1. 修复Redux Reducer的不可变性问题

Redux要求状态更新必须返回新的引用,若直接修改原数组/对象,组件无法感知状态变化,导致拖拽结果不生效。

错误示例(直接修改原状态):

case 'SAVE_DSR_ORDER':
  state.dsrItems = action.payload;
  return state;

正确写法(返回新状态引用):

case 'SAVE_DSR_ORDER':
  return {
    ...state,
    dsrItems: [...action.payload] // 浅拷贝数组生成新引用
  };

2. 同步本地状态与Redux状态

拆分后,DrsTargetComponent需同时维护本地拖拽状态(实时预览排序)和Redux状态(保存提交),确保初始化与保存时状态一致:

import { useSelector, useDispatch } from 'react-redux';
import { useState, useEffect } from 'react';

const DrsTargetComponent = () => {
  const reduxDsrItems = useSelector(state => state.dsr.dsrItems);
  const dispatch = useDispatch();
  // 本地状态用于拖拽实时更新,初始值从Redux同步
  const [localItems, setLocalItems] = useState([...reduxDsrItems]);

  // 拖拽结束更新本地状态
  const handleDragEnd = (result) => {
    if (!result.destination) return;
    const items = Array.from(localItems);
    const [reorderedItem] = items.splice(result.source.index, 1);
    items.splice(result.destination.index, 0, reorderedItem);
    setLocalItems(items);
  };

  // 保存时将本地状态同步到Redux
  const handleSave = () => {
    dispatch({ type: 'SAVE_DSR_ORDER', payload: localItems });
  };

  // 删除项更新本地状态
  const handleDelete = (itemId) => {
    setLocalItems(prev => prev.filter(item => item.id !== itemId));
  };

  // 监听Redux状态变化,外部修改时同步到本地
  useEffect(() => {
    setLocalItems([...reduxDsrItems]);
  }, [reduxDsrItems]);

  return (
    <div>
      {localItems.map(item => (
        <div key={item.id}>
          {item.name}
          <button onClick={() => handleDelete(item.id)}>x</button>
        </div>
      ))}
      <button onClick={handleSave}>Save</button>
    </div>
  );
};

3. 确保拖拽组件的上下文传递(若使用react-beautiful-dnd)

如果用react-beautiful-dnd,需确保DragDropContext包裹所有拖拽相关组件(包括SideBar内的EditDsrSubWidgets和外部的DrsTargetComponent),否则拖拽事件无法跨组件传递:

import { DragDropContext } from 'react-beautiful-dnd';

const App = () => {
  return (
    <DragDropContext onDragEnd={handleDragEnd}>
      <SideBar>
        <EditDsrSubWidgets />
      </SideBar>
      <DrsTargetComponent />
    </DragDropContext>
  );
};

4. 优化组件props传递,避免不必要重渲染

拆分后若组件因props频繁变化丢失拖拽状态,可使用React.memo包裹子组件,或用useCallback缓存回调函数:

对EditDsrSubWidgets(拖拽源):

import { memo, useCallback } from 'react';
import { Draggable } from 'react-beautiful-dnd';

const EditDsrSubWidgets = memo(({ items, onAddItem }) => {
  const handleAdd = useCallback((item) => {
    onAddItem(item);
  }, [onAddItem]);

  return (
    <div>
      {items.map((item, index) => (
        <Draggable key={item.id} draggableId={item.id} index={index}>
          {(provided) => (
            <div
              {...provided.draggableProps}
              {...provided.dragHandleProps}
              ref={provided.innerRef}
              onClick={() => handleAdd(item)}
            >
              {item.name}
            </div>
          )}
        </Draggable>
      ))}
    </div>
  );
});

父组件传递onAddItem时:

const handleAddItem = useCallback((newItem) => {
  setLocalItems(prev => [...prev, newItem]);
}, []);

<EditDsrSubWidgets items={availableItems} onAddItem={handleAddItem} />

5. 检查删除功能的状态更新逻辑

删除按钮失效通常是因为删除时未正确更新本地状态,或Redux同步覆盖了本地修改。确保删除操作仅修改本地状态,保存时再同步到Redux(如上述handleDelete示例),避免直接修改Redux状态导致状态不一致。


内容的提问来源于stack exchange,提问作者Denys Linetskyi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:15:55