Vue中useVirtualList删除元素导致相邻元素状态错误的问题如何解决?

新手上路,请多包涵

我使用了虚拟列表useVirtualList
cacheLike_list的内容会在进入软件自动从数据库获取

const cacheLike_list = ref([])
const {list, containerProps, wrapperProps} = useVirtualList(
  cacheLike_list.value,
  {
    itemHeight: 56,
    overscan: 5,
  })

我想实现一个类似收藏夹的功能,即:当取消收藏后,从收藏夹缓存移除,并从虚拟列表内移除,使其不渲染,关键代码(在循环内):

<label class="swap">
<input ref="theme_sw" :checked="metadata.data.isLike" class="outline-none" type="checkbox"
                   @change="SwitchLikes($event,{path:metadata.data.path,index:metadata.index})"/>
<svg class="swap-off" height="16" viewBox="0 0 24 24">
  空心
</svg>
<svg class="swap-on" height="16" viewBox="0 0 24 24">
  红心
</svg>
</label>

SwitchLikes方法:

function SwitchLikes(event, args) {
  console.log('likes: ', toRaw(cacheLike_list.value[args.index]))
  db.init_DB().then(() => {
    db.deleteData(args.path, 'LikesCache')
    cacheLike_list.value[args.index].isLike = false
    db.addData(toRaw(cacheLike_list.value[args.index]))
    db.close_db()
    cacheLike_list.value.splice(args.index, 1)
    EventBus.emit('set_Like_false', args.path)
  })
}

问题:

假如现在我想移除第一首歌,正常来说点击爱心后它应该从列表中移除,但我这发生的现象是:它确实从列表中移除了,也正确的从数据库中删除了,但是在这首被取消收藏的歌的下一首歌的爱心也取消被选中了(即红心变成空心的了),检查元素,发现input状态依旧是checked:

我使用的css框架是tailwindcss,也使用了daisyUI,我猜测可能是在删除虚拟列表中数据时,下一个元素的状态被错误的设置了,或者是鼠标事件被错误的触发了,但是仍然找不到解决方法
组件链接swap

阅读 1.4k
avatarAI BotBETA

解决方案

在 Vue 中使用 useVirtualList 时,直接修改列表项的状态并随后从列表中删除该项可能会导致渲染问题,因为虚拟列表依赖于一个静态的数组索引来渲染元素。当你删除一个元素时,后续元素的索引会发生变化,这可能会导致渲染或状态更新上的不一致。

为了解决这个问题,你可以采取以下步骤:

  1. 更新状态前先复制数据:在修改数据之前,先复制一份数据的引用,避免直接修改原始数组。
  2. 使用 Vue 的响应式方法来更新数据:利用 Vue 的响应式机制,比如使用 splice 方法或其他能触发响应式更新的方法。
  3. 确保事件处理逻辑正确:在事件处理中,确保逻辑正确,避免不必要的重渲染或状态错误。

具体修改如下:

  • SwitchLikes 方法中,先更新 isLike 状态,然后再从列表中移除该项。
  • 使用 Vue 的 splice 方法来移除项,这样可以确保响应式系统能够正确更新。

修改后的 SwitchLikes 方法:

function SwitchLikes(event, args) {
  console.log('likes: ', toRaw(cacheLike_list.value[args.index]))
  db.init_DB().then(() => {
    // 先更新数据库和缓存中的状态
    const item = { ...cacheLike_list.value[args.index] };
    item.isLike = false;
    db.deleteData(args.path, 'LikesCache');
    db.addData(item);

    // 更新缓存列表的状态
    Vue.set(cacheLike_list.value, args.index, { ...item });

    // 等待数据库操作完成后,再从列表中移除该项
    cacheLike_list.value.splice(args.index, 1);

    db.close_db();
    EventBus.emit('set_Like_false', args.path);
  })
}

注意

  • 这里使用了 Vue.set 来确保更新能够触发响应式系统。如果你使用的是 Vue 3,可以使用 reactiveref 的相应方法来确保响应性。
  • 确保在更新状态后,再从列表中移除该项,以避免索引变化导致的问题。

此外,检查你的 CSS 和框架(如 TailwindCSS 和 DaisyUI)是否有可能影响到输入控件的状态。在某些情况下,CSS 动画或过渡效果可能会延迟状态的更新,导致视觉上出现不一致。如果问题依旧存在,可以考虑简化问题,逐步排除可能的干扰因素。

1 个回答
✓ 已被采纳

我感觉是 cacheLike_list.value.splice(args.index, 1) 修改了数组,这可能导致 响应式系统没有正确更新虚拟列表的渲染

function SwitchLikes(event, args) {
  console.log('likes: ', toRaw(cacheLike_list.value[args.index]));
  db.init_DB().then(() => {
    db.deleteData(args.path, 'LikesCache');

    // 更新数据响应式
    const removedItem = toRaw(cacheLike_list.value[args.index]);
    cacheLike_list.value = cacheLike_list.value.filter((_, index) => index !== args.index);

    db.addData(removedItem);
    db.close_db();

    // 强制刷新虚拟列表
    list.refresh();

    EventBus.emit('set_Like_false', args.path);
  });
}

useVirtualList 通常提供了 remove 或 refresh 方法来手动更新虚拟列表。可以在删除元素后,调用 list.refresh() 强制更新虚拟列表

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏