React Query使用setQueryData如何更新select转换后的数据?
问题原因
你遇到这个报错的核心原因是对select配置的作用理解有偏差:
select是数据读取阶段的派生转换函数,不会修改TanStack Query缓存中存储的原始值。- 你的缓存里实际存的始终是Firebase返回的原始快照对象,每次组件读取
data的时候,Query内部才会拿缓存里的原始快照跑你写的转换逻辑,生成fullDaysArray再返回给你。 - 你直接把转换后的数组通过
setQueryData写入缓存后,下次读取时select函数接收到的参数就是这个普通数组,自然找不到docs属性,执行data.docs.map就会抛出类型错误。
推荐解决方案
最直接、可维护性最高的方案是把数据转换逻辑前移到queryFn中,让缓存直接存储你业务层需要的Day[]结构,不需要额外用select做转换。
代码调整示例
原写法(缓存原始快照,靠select转换)
{ queryKey: ["currentDiary"], queryFn: () => getDocs(diaryCollectionRef), // 返回Firebase原始快照 select: (data) => { let fullDaysArray = [] as Day[]; data.docs.map((docSnapshot) => { const { id } = docSnapshot; let data = { ...docSnapshot.data(), documentId: id, } as Day; fullDaysArray.push(data); }); fullDaysArray.sort((a, b) => a.order - b.order); return fullDaysArray; }, }
修改后写法(queryFn直接返回目标结构,缓存存转换后的数据)
{ queryKey: ["currentDiary"], queryFn: async () => { const querySnapshot = await getDocs(diaryCollectionRef); // 直接在queryFn里完成转换,逻辑和原来的select完全一致 const fullDaysArray = querySnapshot.docs.map((docSnapshot) => ({ ...docSnapshot.data(), documentId: docSnapshot.id, } as Day)).sort((a, b) => a.order - b.order); return fullDaysArray; }, // 移除select配置 }
调整后你原来的更新代码可以直接使用,不会再报错:
// newData直接传Day[]类型的数组即可 queryClient.setQueryData("currentDiary", newData);
不推荐的方案说明
如果你因为特殊场景必须保留原始Firebase快照在缓存中(比如同一份原始数据需要被多个不同的select派生为不同结构复用),那调用setQueryData时就必须传入符合Firebase快照结构的完整对象,手动构造这个对象成本很高,还容易和Firebase SDK的内部结构冲突产生兼容问题,没有特殊需求不要选这条路。
另外提一个代码规范小问题:你原来的转换逻辑里用map遍历却没有利用map的返回值,而是往外部数组push,属于API误用,直接用map生成转换后的数组再排序的写法更简洁语义也更准确,就是上面示例里的写法。
内容的提问来源于stack exchange,提问作者Ripas55
相关产品推荐
相关产品推荐

