Firestore updateDoc单次请求更新两个数组失败问题排查
问题:Firestore单次updateDoc同时操作数组时,原数组无法移除元素
我在React中开发一个简单的任务应用,使用Firestore存储数据。需求是将任务标记为已完成时,从pending数组移除并添加到performed数组,反之亦然。最初两次调用updateDoc可正常实现功能,但改为单次调用updateDoc同时操作两个数组时,任务能添加到目标数组,却无法从原数组移除。
初始可行代码
const docRef = doc(db, 'users', uid) const markDone = async task => { setIsReading(true) const { updateDoc, arrayUnion, arrayRemove } = await import('firebase/firestore') if (task.done === false) { await updateDoc(docRef, { 'listOfTask.pending': arrayRemove(task)}) task.done = !task.done await updateDoc(docRef, { 'listOfTask.performed': arrayUnion(task)}) } else { await updateDoc(docRef, { 'listOfTask.performed': arrayRemove(task) }) task.done = !task.done await updateDoc(docRef, { 'listOfTask.pending': arrayUnion(task) }) } await readData() }
修改后的问题代码
const markDone = async task => { setIsReading(true) const { updateDoc, arrayUnion, arrayRemove } = await import('firebase/firestore') const t = task //Clone the task object if (task.done === false) { t.done = !t.done //Change value to true in done property in cloned task await updateDoc(docRef, { 'listOfTask.pending': arrayRemove(task), //Remove original task in pending 'listOfTask.performed': arrayUnion(t) //Add cloned task in performed }) } else { t.done = !t.done await updateDoc(docRef, { 'listOfTask.pending': arrayUnion(task), 'listOfTask.performed': arrayRemove(t) }) } await readData() }
数据结构:文档中listOfTask包含pending和performed两个数组,分别存储待办和已完成任务。
解决方案
问题根源在于对象引用和Firestore数组匹配规则:
const t = task只是复制了对象引用,并非真正克隆,修改t.done时会同步改变原task对象的done值。- Firestore的
arrayRemove依赖完全匹配对象内容来移除元素,单次update中,原task的done值已经被修改,导致arrayRemove(task)无法匹配数组中原始的未修改元素,自然无法移除。
正确修改代码
需要真正克隆任务对象,确保原对象与克隆对象相互独立:
const docRef = doc(db, 'users', uid) const markDone = async task => { setIsReading(true) const { updateDoc, arrayUnion, arrayRemove } = await import('firebase/firestore') // 浅克隆对象(无嵌套结构时适用),嵌套结构改用JSON.parse(JSON.stringify(task))深克隆 const t = { ...task } if (task.done === false) { t.done = !t.done await updateDoc(docRef, { 'listOfTask.pending': arrayRemove(task), // 原task未被修改,匹配pending数组中的原始元素 'listOfTask.performed': arrayUnion(t) // 克隆对象t已修改done值,添加到performed }) } else { t.done = !t.done await updateDoc(docRef, { 'listOfTask.pending': arrayUnion(t), // 克隆对象t已修改done值,添加到pending 'listOfTask.performed': arrayRemove(task) // 原task未被修改,匹配performed数组中的原始元素 }) } await readData() }
这样修改后,原task对象的属性保持不变,arrayRemove能精准匹配数组中的原始元素,单次update即可同时完成移除和添加操作。
内容的提问来源于stack exchange,提问作者user20294238
相关产品推荐
相关产品推荐

