首次点击发布按钮时Firestore未存储Firebase Storage图片URL求助
Firebase Storage下载链接无法同步写入Firestore的问题
我尝试把Firebase Storage生成的downloadLink存储到Firestore里,但现在所有数据和图片链接只有第二次点击“发布”按钮时才能成功写入。我知道这是异步函数的问题,但没足够经验解决。
在createPost函数里用console.log("I am the URL: {url}")调试,uploadFile函数里用console.log("look at me {url}")调试,发现前者输出空值,后者过一会儿才输出正确的URL,导致setDoc()把imageLink存成了空字符串。
请问怎么解决?另外求能帮我理解异步函数的相关说明。
我的代码
const PostModal = (props) => { const makeid = (length) => { var result = ''; var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var charactersLength = characters.length; for ( var i = 0; i < length; i++ ) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } const [descriptionText, setDescriptionText] = useState(""); const [addressText, setAddressText] = useState(""); const [venueText, setVenueText] = useState(""); const [startTimeText, setStartTimeText] = useState(""); const [endTimeText, setEndTimeText] = useState(""); const [shareImage, setShareImage] = useState(""); const [videoLink, setVideoLink] = useState(""); const [assetArea, setAssetArea] = useState(""); const [url, setURL] = useState(""); const { data } = useSession(); const storage = getStorage(); const storageRef = ref(storage, `images/${makeid(5) + shareImage.name}`); const uploadFile = () => { if (shareImage == null) return; uploadBytes(storageRef, shareImage).then( (snapshot) => { //console.log("Image uploaded") getDownloadURL(snapshot.ref).then( (URL) => { setURL(URL); console.log(`look at me: ${URL}`)}); }); } const createPost = async () => { var idLength = makeid(25); const uploadTask = uploadBytesResumable(storageRef, file); uploadFile() console.log(`I am the URL: ${url} `) setDoc(doc(db, "posts", idLength), { eventDescription: descriptionText, eventAddress: addressText, venueName: venueText, startTime: startTimeText, endTime: endTimeText, imageLink: url, videoLink: videoLink, username: data.user.name, companyName: !data.user.company ? "" : data.user.company, timestamp: Timestamp.now(), }); } const handleChange = (e) => { const image = e.target.files[0]; if(image === '' || image === undefined) { alert('not an image, the file is a ${typeof image}'); return; } setShareImage(image); }; const switchAssetArea = (area) => { setShareImage(""); setVideoLink(""); setAssetArea(area); }; const reset = (e) => { setDescriptionText(""); setAddressText(""); setVenueText(""); setStartTimeText(""); setEndTimeText(""); setShareImage(""); setVideoLink(""); setURL(""); props.handleClick(e); };
问题根源
uploadFile是异步操作,但你现在调用它后立刻执行setDoc,此时图片还没上传完成,url状态还没更新,所以存了空值。第二次点击时,url已经是上次上传后的正确值,所以能成功。
解决步骤
1. 让uploadFile返回Promise
把uploadFile改成返回Promise,这样就能用await等待它完成,直接拿到下载URL,不用依赖状态更新:
const uploadFile = () => { if (!shareImage) return Promise.resolve(null); // 没有图片时返回空 // 每次上传时创建存储引用,避免组件渲染时shareImage为空报错 const storageRef = ref(storage, `images/${makeid(5) + shareImage.name}`); return uploadBytes(storageRef, shareImage).then((snapshot) => { return getDownloadURL(snapshot.ref); }); }
2. 在createPost中等待上传完成
因为createPost已经是async函数,直接用await等待uploadFile的结果,拿到URL后再写入Firestore:
const createPost = async () => { var idLength = makeid(25); // 等待图片上传完成,拿到URL const imageUrl = await uploadFile(); console.log(`I am the URL: ${imageUrl} `) // 写入Firestore,用拿到的imageUrl await setDoc(doc(db, "posts", idLength), { eventDescription: descriptionText, eventAddress: addressText, venueName: venueText, startTime: startTimeText, endTime: endTimeText, imageLink: imageUrl || "", // 没有图片时存空字符串 videoLink: videoLink, username: data.user.name, companyName: data.user.company || "", timestamp: Timestamp.now(), }); // 写入完成后重置状态 reset(); }
3. 异步函数核心逻辑说明
- 异步操作(比如文件上传、网络请求)不会阻塞代码执行,会在后台完成,完成后通过
then或await返回结果。 await必须在async函数里使用,作用是暂停代码执行,直到Promise完成,拿到结果后再继续执行后面的代码。- 不要依赖React状态更新来传递异步操作的结果(比如用
setURL),因为状态更新是异步的,无法立刻拿到新值,直接返回结果更可靠。
内容的提问来源于stack exchange,提问作者FriscoFrank98
相关产品推荐
相关产品推荐

