如何在RTK Query中排序缓存数组?拖拽重排货架遇Immer代理问题
问题解决与Immer代理数组交互指南
一、修复乐观更新的排序问题
你的核心问题是直接给draft赋值新数组不会生效——Immer的draft是代理对象,无法通过draft = sortedShelves这种方式替换整个数组,必须通过原地修改draft内容让Immer追踪变化。
方案1:直接操作原draft(推荐)
无需克隆数组,直接在代理数组上更新order并原地排序:
async onQueryStarted(shelvesToReorder, {dispatch, queryFulfilled}) { const patchResult = dispatch( firestoreApi.util.updateQueryData('getShelves', undefined, draft => { // 1. 更新对应货架的order值 shelvesToReorder.forEach(({id, index}) => { const shelf = draft.find(item => item.id === id); if (shelf) { shelf.order = index; } }); // 2. 原地排序(sort是原地修改方法,Immer可追踪) draft.sort((a, b) => a.order - b.order); }) ); try { await queryFulfilled; } catch { patchResult.undo(); } }
方案2:用拷贝数组后替换原draft内容
如果坚持使用克隆数组,可通过splice原地替换draft的全部元素:
async onQueryStarted(shelvesToReorder, {dispatch, queryFulfilled}) { const patchResult = dispatch( firestoreApi.util.updateQueryData('getShelves', undefined, draft => { const shelvesCopy = draft.slice(); shelvesToReorder.forEach(({id, index}) => { const shelf = shelvesCopy.find(item => item.id === id); if (shelf) { shelf.order = index; } }); shelvesCopy.sort((a, b) => a.order - b.order); // 清空原draft并插入排序后的数组 draft.splice(0, draft.length, ...shelvesCopy); }) ); try { await queryFulfilled; } catch { patchResult.undo(); } }
二、Immer代理数组交互规则
- 原地修改方法会被追踪:
sort、push、pop、splice、reverse这类直接修改数组本身的方法,Immer代理能捕捉到变化。 - 返回新数组的方法不自动生效:
slice、map、filter这类返回新数组的方法,若不把新数组内容合并回原draft(比如用splice),draft不会发生变化。 - 禁止直接替换draft引用:draft是Immer创建的代理对象,
draft = newArray只是修改了函数内部的变量引用,并未修改代理背后的原始状态,完全无效。
三、调试代理数组状态
直接console.log(draft)会输出Immer的代理对象,看不到真实数据,需使用Immer的current方法:
import { current } from '@reduxjs/toolkit'; // RTK已集成Immer,直接导入即可 // 在updateQueryData回调中使用 console.log(current(draft)); // 输出数组的真实当前状态
内容的提问来源于stack exchange,提问作者AndyEvery
相关产品推荐
相关产品推荐

