Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upload file stuck on 'uploading' status with customRequest Ant Design (antd)

I really need your help with uploading files using Ant Design (antd). I don't need request action with upload component, therefore I use the onSuccess() function in customRequest to skip fetch, but onChange method the status stucks only on 'uploading'. It doesn't go to 'done' status. I need your help, please

Sandbox link: https://codesandbox.io/s/runtime-platform-znyow?file=/src/App.js

    import React, { useState } from "react";
    import "./styles.css";
    import { Button, Upload } from "antd";

    export default function App() {
      const [fileList, setFileList] = useState([]);

      const fileProps = {
        action: "",
        name: "file",
        multiple: true,
        customRequest(e) {
          e.onSuccess();
        },
        onChange(info) {
          const { status } = info.file;
          console.log(status); // always 'uploading'
          if (status === "done") {
            // this part is unreachable
            let fileList = [...info.fileList];
            setFileList(fileList);
          }
        },
        onRemove: (file) => {
          setFileList((prevState) => prevState.filter((d) => d.uid !== file.uid));
        }
      };

      return (
        <div className="App">
          <Upload {...fileProps} fileList={fileList}>
            <Button type="primary">Attach file</Button>
          </Upload>
        </div>
      );
    }
like image 666
Saulet Yskak Avatar asked Oct 18 '25 19:10

Saulet Yskak


2 Answers

i've had similiar problem after updating version of antd. Here's the code how i fix this without sending request.

    const handleChange = useCallback((info) => {
    if (info.file.status === 'uploading') {
        setImage({ loading: true, image: null });
        info.file.status = 'done';
    }
    if (info.file.status === 'done') {
        getBase64(info.file.originFileObj, (imageUrl) => {
            const img = new Image();
            img.src = imageUrl;
            img.addEventListener('load', function () {
                setImage({ loading: false, image: imageUrl });
                setFileList([{ ...info.fileList[0] }]);
            });
        });
    }
}, []);

inside condition (info.file.status === 'uploading') i've changed info.file.status === 'done' and surprsingly it work. Probably there's better solution to solve this, but maybe this will help

like image 85
Bastek Avatar answered Oct 20 '25 10:10

Bastek


I have been banging my head against the wall for hours, and you won't believe what the fix to this is. Set fileList to undefined on the Upload component if the array is empty, which was mine and your default state value.

return (
    <div className="App">
      <Upload {...fileProps} fileList={fileList.length > 0 ? fileList : undefined}>
        etc
      </Upload>
    </div>
  );

As soon as I did that I was getting status "done" in my onChange handler.

I was tipped off by this comment in an AntD issue.


Edit: It appears that if you want to upload again after an initial batch, the fileList prop has to stay undefined, which means even more work on our end...

In case it helps anyone, here is the working hook I made in our project to manage this. I wish it had half the React state tracking everything, but seems necessary in my case with this limitation of antd.

const useUpload = ({
  fileBucket,
  setFileList,
  fileList,
}) => {
  const [updatedFiles, setUpdatedFiles] = useState([]);
  const [completed, setCompleted] = useState(0);
  const [fileTotal, setFileTotal] = useState(0);

  const storage = getStorage();

  // Only update the file list when all files have their downloadUrl fetched
  useEffect(() => {
    if (fileTotal === 0) {
      return;
    }

    if (fileTotal === completed) {
      setFileList([...fileList, ...updatedFiles]);
      setCompleted(0);
      setFileTotal(0);
    }
  }, [completed, fileTotal, setFileList, updatedFiles, fileList]);

  // antd cancels upload on a return of false
  const beforeUpload = async (file) => {
    // ... not relevant  
  };

  const customRequest = ({ onError, onSuccess, file }) => {
    const storageRef = ref(
      storage,
      `${fileBucket}/${new Date()} -- ${generateRandomId()}`
    );

    uploadBytes(storageRef, file)
      .then((snapshot) => getDownloadURL(snapshot.ref))
      .then((downloadUrl) => {
        onSuccess({ downloadUrl });
      })
      .catch(() => {
        onError();
      });
  };

  const onChange = async ({ file, fileList }) => {
    setFileTotal(fileList.length);

    if (file.status === "done") {
      const { downloadUrl } = file.response;

      // Using the callback function on setState functions helps deal with race conditions.
      // We still need to delay the update until all the files have loaded, hence the regretable two state values.
      setUpdatedFiles((prevState) => {
        const fileIndex = fileList.findIndex((fileInList) => {
          return fileInList.uid === file.uid;
        });

        return [
          ...prevState,
          {
            ...file,
            url: downloadUrl,
            pageNumber: fileIndex + 1,
          },
        ];
      });
      setCompleted((prevState) => prevState + 1);

      message.success(`${file.name} file uploaded successfully`);
    }

    if (file.status === "error") {
      message.error(`${file.name} file upload failed.`);
    }
  };
  return { beforeUpload, onChange, customRequest };
};
like image 27
tannerbaum Avatar answered Oct 20 '25 09:10

tannerbaum