You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
           )
         }
        }]
问题分析与修复方案

核心问题原因

  1. 两次点击才生效:findImages是异步函数,调用后直接执行deleteImages,但React状态更新是异步的,此时images还未被更新为最新获取的图片路径,deleteImages使用的是旧的images值(初始空数组),导致第一次删除失败;第二次点击时images已更新,才能正确执行删除。

  2. 首次报错:

    • 初始错误:images初始为空数组,首次调用时photos.toString()得到空字符串,photoName截取后为无效值甚至undefined,导致创建的Storage引用路径非法,触发childPath.split错误。
    • 修改后的错误:photoName可能为空字符串,导致创建了根引用('/'),Firebase Storage不允许直接对根引用执行删除操作,必须指定具体文件路径。
  3. 第二次报错:首次点击已删除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 13:15:34