跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

如何正确使用 React 的 useState 更新上传计数器?

在 Firebase 文件批量上传中,直接用 setCount(count + 1) 无法正确递增计数器,因其闭包捕获了初始值;必须使用函数式更新 setCount(prev => prev + 1),并确保仅在每个文件上传完成时调用一次,才能准确反映已完成上传数量。 在 firebase 文件批量上传中,直接用 setcount(count + 1) 无法正确递增计数器,因其闭包捕获了初始值;必须使用函数式更新 setcount(prev => prev + 1),并确保仅在每个文件上传完成时调用一次,才能准确反映已完成上传数量。 在 React 中处理异步批量上传(如 Firebase Storage 的 uploadBytesResumable)时,一个常见误区是误用 useState 的 setter 函数。你可能期望每次上传任务进入 "running" 状态时调用 setCount(count + 1) 来累加进度,但实际结果却是: 计数器始终只增加到 1,或出现远超预期的异常值 。根本原因在于:count 是闭包变量,在 uploadFiles 函数执行时被“冻结”,后续所有 setCount(count + 1) 实际都等价于 setCount(0 + 1) —— 即永远设为 1。 ✅ 正确做法: 始终使用函数式更新(functional update) React 的 setState(如 setCount)支持传入一个函数,该函数接收前一状态作为参数。这能确保获取到最新值,避免闭包 stale state 问题:
setCount(prev => prev + 1); // ✅ 安全、可靠、并发安全
⚠️ 但仅改用函数式更新还不够!另一个关键陷阱是:"state_changed" 回调会在上传过程中 高频触发 (尤其大文件),而 "running" 状态可能多次进入。若在此处调用 setCount,会导致计数器被重复累加(例如单个文件上传触发 5 次 "running",就加 5 次),完全偏离“已成功完成上传的文件数”这一业务目标。 ? 真正语义正确的时机是: 每个文件上传彻底完成(onComplete)时,仅增加 1 次 。Firebase 的 uploadTask.on() 第三个参数正是 onComplete 回调,它保证仅在上传成功后执行一次:
uploadtask.on( "state_changed", (snapshot) => { // 进度更新逻辑(可选:用于 UI 进度条) }, (error) => { console.error("Upload failed:", error); // ❌ 不在此处更新 count:失败不应计入完成数 }, () => { // ✅ 仅在此处更新:一个文件确认上传成功 setCount(prev => prev + 1); } );
? 补充注意事项: React Native For Android 源码编译 中文WORD版 本文档主要讲述的是React Native For Android 源码编译;希望对大家会有帮助;感兴趣的朋友可以过来看看 下载 不要在 "running" 分支中调用 setuploacomplete("complete") :这会误导用户以为整个批次已完成,应在所有文件全部处理完毕(包括成功/失败汇总)后统一设置。 错误处理需独立统计 :若需显示失败数,应维护单独的 setErrorCount 状态,而非复用 count。 避免并发竞态导致的计数偏差 :即使使用函数式更新,多个异步任务同时调用 setCount(prev => prev + 1) 仍是安全的(React 会自动批处理),但逻辑上仍应严格限定调用位置(即 onComplete)。 进阶建议 :对于复杂上传场景(如需每个文件独立进度、重试、取消),推荐拆分为 UploadTask 子组件 + useReducer 或 Zustand 管理全局上传队列,而非在自定义 Hook 中混杂状态与副作用逻辑。 最终修正后的核心逻辑如下(精简版):
const useUploader = (data, foldername) => { const [uploadStatus, setUploadStatus] = useState(""); // 替代 upLoadcomplete(更语义化命名) const [completedCount, setCompletedCount] = useState(0); const uploadFiles = async () => { setCompletedCount(0); // 重置计数器 setUploadStatus("uploading"); for (let i = 0; i < data.length; i++) { const name = `${v4()}`; const location = `${foldername}/${name}`; const metadata = { customMetadata: { uuid_name: name } }; const dataRef = ref(proStorage, location); const task = uploadBytesResumable(dataRef, data[i], metadata); task.on( "state_changed", (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`File ${i + 1}: ${progress.toFixed(1)}%`); }, (error) => { console.error(`File ${i + 1} failed:`, error); setUploadStatus("error"); }, () => { // ✅ 关键:仅在此处更新完成数 setCompletedCount(prev => prev + 1); if (completedCount + 1 === data.length) { setUploadStatus("success"); } } ); } }; return [uploadFiles, uploadStatus, completedCount]; }; export default useUploader;
通过精准定位状态更新时机 + 函数式更新写法,即可实现清晰、可预测、符合业务语义的上传进度计数。

相关文章