Redux异步拉取Firestore嵌套数据报non extensible object错误
问题根因
报错和异常行为来自两个核心问题:
- 异步逻辑时序错误:
Array.prototype.forEach本身不会等待内部async回调的异步操作执行完成,外层查询完contacts主文档后会直接走后续dispatch逻辑,此时嵌套的notes子集合查询还没跑完。Redux Toolkit开发环境下会通过Immer冻结已提交到store的状态对象,后续异步回调再往contacts数组里push数据时,操作的是已经被冻结的不可扩展对象,直接触发 non extensible object 报错。这也是你移除notes查询逻辑后报错消失的原因:没有嵌套异步等待时,forEach会同步完成所有contacts数据的push,再执行dispatch,后续没有代码再修改提交到store的对象,自然不会触发异常。 - 你之前尝试的
copyContacts = contacts属于引用赋值,不是数组拷贝,两个变量指向内存中同一个数组地址,完全起不到隔离作用,自然没法规避报错,作用域访问问题也是写法位置错误导致的。
修复方案
不要用forEach处理异步并发查询,改用map配合Promise.all,等所有contact对应的notes查询全部完成、数据全部组装完毕后,再执行dispatch提交状态。
修复后的完整代码如下:
export const fetchContactData = () => { return async dispatch => { try { // 先查询所有contact主文档 const contactDocsSnap = await getDocs(collection(db, 'contacts')); // 等待所有contact关联的notes查询全部完成,同步组装完整数据 const contacts = await Promise.all( contactDocsSnap.docs.map(async (doc) => { const notesSnap = await getDocs(collection(db, 'contacts', doc.id, 'notes')); const notesArray = notesSnap.docs.map(note => ({ ...note.data(), id: note.id })); return { ...doc.data(), id: doc.id, notes: notesArray } }) ); // 所有数据组装完成后再提交到store dispatch(contactActions.replaceContactData({ contacts })); } catch (error) { console.log(error) dispatch( uiActions.showNotification({ status: 'error', title: 'Error', message: 'Error loading contacts.' }) ); }; }; };
修复后逻辑会严格按正确时序执行:先拉取所有contact主文档列表,再并发查询每个contact关联的notes,等所有数据都组装为全新的普通数组、普通对象后,才一次性提交到Redux,不会再出现操作冻结对象的问题,也不需要额外做数组拷贝。
内容的提问来源于stack exchange,提问作者joshua crowe
相关产品推荐
相关产品推荐

