React useState中fileInfoDoC显示undefined?如何存储docId?
问题原因与解决方案
你的代码里出现fileInfoDoC始终为undefined的问题,主要有3个核心原因:
1. useState更新是异步的,无法立即获取最新值
setFileInfoDoc(docID)是异步操作,调用完立刻console.log(fileInfoDoC)只能拿到更新前的旧值(初始的undefined)。你不需要在saveInfo里打印这个状态,直接用本地变量docID就足够完成数据库写入。
2. uploadCompleted的触发时机过早
目前在上传进度到100%时就设置uploadCompleted=true,但此时getDownloadURL和saveInfo可能还没执行完毕,fileInfoDoC还没被赋值,导致useEffect触发时拿不到正确的docId。
3. useEffect依赖数组错误
你写的[uploadCompleted==true]是一个表达式,每次渲染都会重新计算,导致useEffect无法正确监听uploadCompleted的状态变化,应该直接把uploadCompleted作为依赖项。
修改后的完整代码
"use client" import React, { useEffect, useState } from 'react' import UploadForm from './_components/UploadForm' import { app } from '@/firebaseConfig' import { getStorage, ref ,uploadBytesResumable,getDownloadURL} from "firebase/storage"; import { doc, getFirestore, setDoc } from "firebase/firestore"; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import { useUser } from '@clerk/nextjs'; import { generateRandomString } from '@/app/_utils/GenerateRandomString'; import { useRouter } from 'next/navigation'; const Upload = () => { const [fileInfoDoC,setFileInfoDoc] = useState() const router = useRouter() const {user} = useUser(); const [progress, setprogress] = useState() const [uploadCompleted,setUploadCompleted] = useState(false); const storage = getStorage(app) const db = getFirestore(app); const uploadFile = (SaveFiles) =>{ const metadata = { contentType: SaveFiles.type }; const storageRef = ref(storage, 'fileshare/'+SaveFiles?.name); const uploadTask = uploadBytesResumable(storageRef, SaveFiles, SaveFiles.type); uploadTask.on('state_changed', (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; setprogress(progress) if(progress === 100){ toast.success('Successfully Uploaded', { position: "top-right", autoClose: 5000, hideProgressBar: false, closeOnClick: false, pauseOnHover: true, draggable: true, progress: undefined, theme: "light", }); } // 进度100%时获取下载链接并保存信息 progress === 100 && getDownloadURL(uploadTask.snapshot.ref).then(async (downloadURL) => { console.log('File available at', downloadURL); // 等待saveInfo执行完成后再标记上传完成 await saveInfo(SaveFiles,downloadURL); setUploadCompleted(true); }); }, // 添加错误处理回调,避免上传失败后卡住 (error) => { toast.error('Upload failed: ' + error.message, {theme: "light"}); } ) } const saveInfo = async(file,fileURL)=>{ const docID = generateRandomString(); setFileInfoDoc(docID); // 直接用docID操作,不需要依赖state await setDoc(doc(db, "uploadedFiles", docID), { fileName:file?.name, fileSize:file?.size, fileType:file?.type, fileUrl:fileURL, userEmail:user?.primaryEmailAddress.emailAddress, userName:user?.fullName, password:'', Id:docID, shortUrl:process.env.NEXT_PUBLIC_BASE_URL + docID }) } useEffect(()=>{ if(uploadCompleted && fileInfoDoC){ setTimeout(()=>{ setUploadCompleted(false) router.push(`/file-preview/${fileInfoDoC}`) },2000) } },[uploadCompleted, fileInfoDoC]) // 修改依赖数组,同时监听fileInfoDoC return ( <> <div className='p-5 px-8 md:px-25'> <h2 className='text-[20px] text-center m-5'>Start <strong className='text-primary'>Uploading</strong> File and <strong className='text-primary'>Share</strong> it </h2> <UploadForm uploadBtnClick={(SaveFiles)=>{uploadFile(SaveFiles)}} progressBar={progress}/> </div> <ToastContainer position="top-right" autoClose={5000} hideProgressBar={false} newestOnTop={false} closeOnClick={false} rtl={false} pauseOnFocusLoss={false} draggable pauseOnHover theme="light" /> </> ) } export default Upload
关键修改点说明
- 把
setUploadCompleted(true)移到await saveInfo(...)之后,确保docId已经存入state再触发后续逻辑 - 修正useEffect的依赖数组为
[uploadCompleted, fileInfoDoC],同时监听两个状态,避免跳转时fileInfoDoC还未更新 - 在useEffect里增加
fileInfoDoC的判断,确保有值才执行路由跳转 - 给
uploadTask.on添加错误处理回调,避免上传失败后没有提示
内容的提问来源于stack exchange,提问作者Aniket Sudke
相关产品推荐
相关产品推荐

