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

React中设置下载进度状态:进度无法逐文件更新的问题

React中批量下载文件时进度条实时更新问题修复

修复后的代码

const [progress, setProgress] = useState(0);
const [loading, setLoading] = useState(false);
const [checkedPlayers, setCheckedPlayers] = useState([]);
// 用useRef保存计数,避免闭包捕获陈旧值
const countRef = useRef(0);

const handleDownload = () => {
  console.log("handleDownload");
  setLoading(true);
  countRef.current = 0; // 重置下载计数

  if (selectedPlayers.length > 0) {
    const hr = 3600;
    const signedUrlExpireSeconds = hr * 1;
    const s3 = new S3Client({
      region: region,
      signatureVersion: 'v4',
      credentials: {
        accessKeyId: accessKeyId,
        secretAccessKey: secretAccessKey
      }
    });

    const downloadPromises = selectedPlayers.map(async player => {
      const myKey = player.Key;
      const bucketParams = {
        Bucket: bucket,
        Key: myKey,
      };
      try {
        const url = await getSignedUrl(s3, new GetObjectCommand(bucketParams), { expiresIn: signedUrlExpireSeconds });
        console.log(`Signed URL for player ${player.Key}: ${url}`);
        
        // 等待文件下载完成后再更新进度
        await new Promise((resolve, reject) => {
          new jsDownloader({
            url: url,
            onSuccess: () => resolve(),
            onError: (err) => reject(err)
          });
        });

        // 更新计数和进度
        countRef.current++;
        setProgress(Math.round(countRef.current / selectedPlayers.length * 100));
      } catch (error) {
        console.log(error);
        // 即使下载失败也更新进度(可根据业务需求调整是否跳过)
        countRef.current++;
        setProgress(Math.round(countRef.current / selectedPlayers.length * 100));
      }
    });

    Promise.all(downloadPromises)
      .then(() => {
        setLoading(false);
        setCheckedPlayers([]);
        setProgress(0);
      })
      .catch(error => console.log(error));
  }
};

问题根源与修复说明

原问题原因

  1. 闭包导致的计数陈旧:原代码中count是普通变量,在map的异步函数里,每次调用setProgress时捕获的都是初始的count值,React会批量处理多次setProgress调用,最终只显示最后一次计算结果,导致进度无法实时更新。
  2. 进度更新时机错误:原代码在拿到S3签名URL后就更新进度,但此时文件还未开始下载,进度更新不匹配实际下载状态。

核心修复点

  • 用useRef存储计数:useRef的current属性是可变的,不会被闭包捕获旧值,确保每次更新进度时都能获取到最新的已完成下载数。
  • 等待下载完成再更新进度:通过Promise包装jsDownloader,利用其成功/失败回调,确保在文件实际下载完成(或失败)后再更新进度。
  • 错误场景处理:单个文件下载失败时也更新进度,避免进度停滞,可根据业务需求调整是否跳过失败文件的计数。

内容的提问来源于stack exchange,提问作者sbaden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:05:24