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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 17:54:51