如何在React组件中使用自定义useStorage钩子返回的url值
实现方案
步骤1:先完善useStorage的内部状态声明
你提供的useStorage缺失了内部状态的定义,同时有闭包问题和边界判断缺失,先修正为可正常运行的版本:
import { useState, useEffect } from 'react'; import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage"; import { firebaseStorage, timestamp } from '你的firebase配置文件路径'; // 替换为自己项目的配置引入 export const useStorage = (file: File | null) => { // 补上缺失的内部状态定义 const [progress, setProgress] = useState<number>(0); const [error, setError] = useState<string | null>(null); const [url, setUrl] = useState<string | null>(null); const [createdAt, setCreatedAt] = useState<Date | null>(null); useEffect(() => { // 文件为空时不执行上传逻辑 if (!file) return; const storageRef = ref(firebaseStorage, `upload/${file.name}`); const uploadTask = uploadBytesResumable(storageRef, file); uploadTask.on( "state_changed", (snapshot) => { const percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; setProgress(percentage); console.log("Upload is " + percentage + "% done"); // 修复原代码的闭包问题,直接用当前计算的进度 }, (error) => { setError(error.message); }, () => { getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { console.log("File available at ", downloadURL); setUrl(downloadURL); setCreatedAt(timestamp()); }); } ); // 组件卸载时取消上传,避免内存泄漏 return () => uploadTask.cancel(); }, [file]); return { url, createdAt, error, progress }; };
步骤2:在表单组件中同步url到表单状态
核心逻辑是调用useStorage拿到返回的url后,通过useEffect监听url变化,自动更新到表单状态中:
import { useState, useEffect } from 'react'; import { useStorage } from '你存放useStorage的文件路径'; // 定义表单类型 type FormState = { drinkName: string; description: string; category: string; price: string; file: string | null; // 这里改为存储上传后的url } const 你的表单组件 = () => { const [formState, setFormState] = useState<FormState>({ drinkName: "", description: "", category: "", price: "", file: null, }); // 单独存储用户选择的原始文件,传给useStorage触发上传 const [selectedFile, setSelectedFile] = useState<File | null>(null); // 调用自定义hook获取上传结果 const { url, progress, error } = useStorage(selectedFile); // 监听url变化,上传完成后自动同步到表单状态 useEffect(() => { if (url) { setFormState(prev => ({ ...prev, file: url })); } }, [url]); // 绑定到文件选择框的onChange事件 const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { if (e.target.files?.[0]) { setSelectedFile(e.target.files[0]); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // 提交前做校验,避免未完成上传就提交 if (selectedFile && !formState.file) { alert('文件正在上传中,请稍候再提交'); return; } if (error) { alert(`文件上传失败:${error}`); return; } // 此时formState.file已经是上传完成的url,直接提交即可 console.log(formState); // 后续执行你自己的表单提交逻辑 }; return ( <form onSubmit={handleSubmit}> {/* 其他表单字段省略 */} <input type="file" onChange={handleFileChange} accept="image/*" /> {/* 可选择性展示上传进度,提升用户体验 */} {progress > 0 && progress < 100 && <p>上传进度:{progress.toFixed(2)}%</p>} <button type="submit">提交</button> </form> ) }
可选优化
如果不需要选完文件就自动上传,也可以把上传逻辑移到handleSubmit中,提交时再上传文件、拿到url后再提交整个表单,避免用户选了文件又取消产生无效的存储占用。
内容的提问来源于stack exchange,提问作者AbdulAzeez Olanrewaju
相关产品推荐
相关产品推荐

