Компонент загрузки нескольких файлов React повторно загружает файлы - PullRequest
1 голос
/ 05 мая 2020

Я создаю компонент загрузки файлов в React, используя XHR и immer js. Он показывает прогресс файла по мере его загрузки. Пользователь может выбрать несколько файлов для одновременной загрузки.

Загрузка одного файла выглядит нормально (за исключением того, что после запуска статус должен быть uploading, а не unprocessed). Журнал консоли выглядит так:

starting to upload file1.png (unprocessed)
onProgress file1.png 18% unprocessed
onProgress file1.png 36% unprocessed
onProgress file1.png 55% unprocessed
onProgress file1.png 73% unprocessed
onProgress file1.png 91% unprocessed
onProgress file1.png 100% unprocessed
onComplete
loadEndEventListener

Но когда вы загружаете два файла одновременно, похоже, что файлы загружаются снова и снова.

Например, если я загружаю file1. png и file2.png, журнал консоли выглядит так:

starting to upload file1.png (unprocessed)
starting to upload file2.png (unprocessed)
onProgress file1.png 3% unprocessed
starting to upload file2.png (unprocessed)
onProgress file2.png 5% unprocessed
starting to upload file1.png (unprocessed)
onProgress file1.png 7% unprocessed
starting to upload file2.png (unprocessed)
onProgress file2.png 10% unprocessed
starting to upload file1.png (unprocessed)
onProgress file1.png 3% unprocessed
onProgress file1.png 11% unprocessed
starting to upload file2.png (unprocessed)
onProgress file2.png 16% unprocessed
starting to upload file1.png (unprocessed)
onProgress file2.png 5% unprocessed
onProgress file1.png 3% unprocessed
onProgress file1.png 15% unprocessed
starting to upload file2.png (unprocessed)
onProgress file2.png 10% unprocessed
onProgress file2.png 21% unprocessed
starting to upload file1.png (unprocessed)
onProgress file1.png 7% unprocessed
onProgress file2.png 10% unprocessed
onProgress file1.png 7% unprocessed

...

Вот ссылка на код: https://codesandbox.io/s/eloquent-cache-kwtrp?file= / src / App. js

Вот мой компонент:

import React from "react";
import produce from "immer";

function File({ name, progress }) {
  return (
    <div>
      {name} {progress}%
    </div>
  );
}

export default function Uploader() {
  const [files, setFiles] = React.useState([]);

  function onComplete() {
    console.log("onComplete");
  }

  function loadEndEventListener() {
    console.log("loadEndEventListener");
  }

  function errorEventListener(e) {
    console.log("errorEventListener");
  }

  function abortEventListener(e) {
    console.log("abortEventListener");
  }

  // file: { progress, status, FileBrowserData: { name, size, type } }
  function uploadFile(file) {
    function onProgress(e) {
      if (e.lengthComputable) {
        const percentComplete = (e.loaded / e.total) * 100;
        const index = files.findIndex(_file => _file.FileBrowserData.name === file.FileBrowserData.name);

        console.log(`onProgress ${file.FileBrowserData.name} ${parseInt(percentComplete)}% ${file.status}`);

        setFiles(
          produce(files, draftFiles => {
            draftFiles[index].progress = percentComplete;
            draftFiles[index].status = "uploading";
          })
        );
      }
    }

    const fd = new FormData();
    fd.append("file", file.FileBrowserData);

    const oReq = new XMLHttpRequest();
    oReq.open("POST", "https://webhook.site/12e03896-442d-42ee-8e2a-63a7cb05a9b9");

    oReq.upload.addEventListener("progress", onProgress);
    oReq.upload.addEventListener("load", onComplete);
    oReq.upload.addEventListener("loadend", loadEndEventListener);
    oReq.upload.addEventListener("error", errorEventListener);
    oReq.upload.addEventListener("abort", abortEventListener);

    oReq.send(fd);
  }

  function uploadUnprocessedFiles() {
    files
      .filter(file => file.status === "unprocessed")
      .forEach(file => {
        console.log(`starting to upload ${file.FileBrowserData.name} (${file.status})`);
        uploadFile(file);
      });
  }

  React.useEffect(() => {
    uploadUnprocessedFiles();
  }, [files]);

  function renderFiles() {
    return files.map(file => (
      <File key={file.FileBrowserData.name} name={file.FileBrowserData.name} progress={file.progress} />
    ));
  }

  function populateFilesInState(fileList) {
    const _fileList = Array.from(fileList).slice(0);

    setFiles(() =>
      _fileList.map(file => ({
        progress: 0,
        status: "unprocessed",
        FileBrowserData: file,
      }))
    );
  }

  return (
    <div>
      <h3>File selector:</h3>
      <input
        type="file"
        multiple={true}
        onChange={e => {
          populateFilesInState(e.target.files);
        }}
      />
      <h3>Files:</h3>
      {renderFiles()}
    </div>
  );
}

Любая помощь приветствуется.

...