React中使用dragula拖拽后状态没更新怎么办?
我在用dragula实现卡片拖拽功能,但拖拽完成后状态数组没有同步更新。虽然能看到DOM变化,但console.log显示state还是原来的顺序。
尝试过在dragula选项里设置removeOnSpill: true,也手动调用了setState,但都没用。代码大概是这样:
import dragula from 'dragula';
const CardList = ({ cards, setCards }) => {
useEffect(() => {
const drake = dragula([document.getElementById('card-container')]);
drake.on('drop', (el, target, src, sibling) => {
const newCards = Array.from(target.children).map(card => card.dataset.id);
setCards(newCards); // 这里没触发更新
});
return () => drake.destroy();
}, []);
return (
<div id="card-container">
{cards.map(id => (
<div key={id} data-id={id}>{id}</div>
))}
</div>
);
};
是不是dragula的事件监听和React状态更新机制有冲突?或者需要在某个生命周期里处理?
把状态更新逻辑从dragula的drop事件里抽出来,放到React的生命周期里去管理。具体来说,可以监听dragula的drop事件来获取新的顺序,但不要直接在事件回调里调用setCards,而是通过一个中间变量来暂存新顺序。
修改后的代码大概长这样:
这里的关键点是用了useRef来获取容器元素,避免直接用document.getElementById,更符合React的写法。然后通过两个useState来分离dragula的DOM操作和React的状态更新。
说白了就是让React按自己的节奏来更新状态,别让dragula直接插手。我之前也踩过类似的坑,后来发现这种解耦的方式最稳妥。记得在组件销毁时清理drake实例,不然容易内存泄漏。
把你的useEffect部分改成这样试试:
这里做了几个改动:首先用prev来对比之前的state,避免不必要的渲染;其次确保返回的是一个新数组,因为React需要引用变化才能触发更新。
另外吐槽一句,dragula虽然好用,但和React这种声明式框架确实有点八字不合。如果你后续要维护更复杂的拖拽场景,可以考虑react-dnd或者react-beautiful-dnd这些专门为React设计的库,能少掉不少头发。
记得给每个卡片加上唯一的data-id属性,这个方案依赖这个属性来重建顺序。如果还是有问题,可能是cards本身不是稳定的id集合,那就需要看看父组件传值有没有问题了。