React删除Firebase Storage图片遇两次点击生效及报错问题求助
问题概述
在React项目中使用MUI DataGrid展示Firestore文档(含图片),点击删除按钮经确认后可删除Firestore文档,但删除Firebase Storage中图片时存在以下问题:
- 需点击两次删除按钮才能完成图片删除
- 首次点击报错:
初始代码报错:Unhandled Promise Rejection: TypeError: childPath.split is not a function. (In 'childPath.split('/')', 'childPath.split' is undefined)
修改代码后报错变为:FirebaseError: Firebase Storage: The operation 'deleteObject' cannot be performed on a root reference, create a non-root reference using child, such as .child('file.png') - 第二次点击虽能删除图片,但仍报错:
Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating 'docSnap.data().image')
相关代码
Firebase 查询代码
import { collection, getDocs, addDoc, updateDoc, deleteDoc, doc, query, where, getDoc, orderBy} from 'firebase/firestore' const rapportsPassagesCollectionRef = collection(db,'rapportsPassages') class RapportsPassagesDataService { deleteRapportPassage = (id) => { const rapportPassageDoc = doc(db,"rapportsPassages",id) return deleteDoc(rapportPassageDoc) } getRapportsPassages = () => { const rapportsByDate = query (rapportsPassagesCollectionRef,orderBy("date","asc")) return getDocs(rapportsByDate) } getRapportPassage = (id) => { const rapportPassageDoc = doc(db,"rapportsPassages",id) return getDoc(rapportPassageDoc) } } export default new RapportsPassagesDataService()
图片删除逻辑代码
const [rapports, setRapports] = useState([]) useEffect(() => { getRapports() }, []) const getRapports = async () => { const data = await RapportsPassagesDataService.getRapportsPassages() setRapports(data.docs.map((doc) => ({ ...doc.data(), id: doc.id }))) } const [images, setImages] = useState([]) const findImages = async (id) => { if (id !== undefined && id !== "") { const docSnap = await RapportsPassagesDataService.getRapportPassage(id) setImages(docSnap.data().image) } } const photos = images.toString() const photoName = (photos.substring(69, photos.length - 53)); const deleteImages = () => { const storageRef = ref(storage,'/' + photoName) deleteObject(storageRef).then(() => { console.log('success'); }).catch((error) => { console.log(error) }) } const handleDelete = (id) => { confirmAlert({ title: 'Confirm', message: 'Are you sure you want to delete this document ?', buttons: [ { label: 'Yes', onClick: async () => { findImages(id) deleteImages() await RapportsPassagesDataService.deleteRapportPassage(id) getRapports() } }, { label: 'No', } ] }) } const actionColumn = [{ field: "action", headerName: "", width: 150, renderCell: (params) => { return ( <div className='cellAction'> <button className="deleteButton" onClick={() =>handleDelete(params.row.id)}> </button> </div> ) } }]
问题分析与修复方案
核心问题原因
两次点击才生效:
findImages是异步函数,调用后直接执行deleteImages,但React状态更新是异步的,此时images还未被更新为最新获取的图片路径,deleteImages使用的是旧的images值(初始空数组),导致第一次删除失败;第二次点击时images已更新,才能正确执行删除。首次报错:
- 初始错误:
images初始为空数组,首次调用时photos.toString()得到空字符串,photoName截取后为无效值甚至undefined,导致创建的Storage引用路径非法,触发childPath.split错误。 - 修改后的错误:
photoName可能为空字符串,导致创建了根引用('/'),Firebase Storage不允许直接对根引用执行删除操作,必须指定具体文件路径。
- 初始错误:
第二次报错:首次点击已删除Firestore文档,第二次点击时调用
getRapportPassage(id)获取的文档已不存在,docSnap.data()为undefined,访问.image时触发报错。
修复代码
直接在删除流程中同步处理图片获取与删除,避免依赖React状态,同时修正Storage引用的创建方式:
const handleDelete = (id) => { confirmAlert({ title: '确认删除', message: '你确定要删除这个文档吗?', buttons: [ { label: '是', onClick: async () => { // 1. 获取目标文档的图片数据 const docSnap = await RapportsPassagesDataService.getRapportPassage(id); const imageUrl = docSnap.data()?.image; // 2. 如果存在图片,删除Storage中的文件 if (imageUrl) { try { // 解析Storage文件路径(从完整URL中提取) const urlObj = new URL(imageUrl); // 解码路径并移除Firebase Storage的固定前缀"/o/" const storagePath = decodeURIComponent(urlObj.pathname).replace('/o/', ''); // 创建有效的Storage文件引用 const storageRef = ref(storage, storagePath); await deleteObject(storageRef); console.log('图片删除成功'); } catch (error) { console.error('图片删除失败:', error); } } // 3. 删除Firestore文档 await RapportsPassagesDataService.deleteRapportPassage(id); // 4. 刷新列表 getRapports(); } }, { label: '否' } ] }); }
关键优化点
- 去掉状态依赖:直接在删除流程中异步获取图片路径,避免React状态更新的异步延迟问题,确保删除时使用最新的文档数据。
- 可靠解析路径:用URL对象解析代替硬编码截取字符串,避免因URL格式变化导致的路径提取失败。
- 顺序执行异步操作:用
await确保图片删除完成后再删除文档,避免文档已删除但图片未删除的情况。 - 空值安全处理:使用可选链
?.避免文档不存在或无图片时的报错。 - 正确创建Storage引用:通过解析后的具体文件路径创建引用,而非根引用,符合Firebase Storage的操作要求。
内容的提问来源于stack exchange,提问作者alain
相关产品推荐
相关产品推荐

