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
相关产品推荐
相关产品推荐

