kenpeter
kenpeter

Reputation: 8294

React dropzone: useState, async inside onDropAccepted

Currently I am using react dropzone.

At the very top, I have something like to track how many files user drop into the dropzone.

[files, setFiles] = useState([]);

Later I have these:

const checkBucketLimitSingle = files => {
  if (files.length >= FILE_BUCKET_LIMIT) {
    return false;
  } else {
    return true;
  }
};

const onDropAccepted = useCallback(
  acceptedFiles => {
    // async
    const processAcceptFiles = async () => {
      let singleFile;

      singleFile = acceptedFiles[0];

      if (acceptedFiles.length > 1) {
        setIsMaxFileNum(true);
        return;
      } else {
        // e.g. api upload error
        if (isUploadError) {
          // NOTE: upload error, we empty file buckets
          // But "files" as dependency passed into useCallback, but not reflected after setFiles([])
          setFiles([]);
        }

        // check bucket limit
        const isValidBucketLimit = checkBucketLimitSingle(files);

        if (isValidBucketLimit) {
          // all good, can set
          setFiles([...files, singleFile]);
        } else {
          // go to ui
          return;
        }
      }
    };

    // fire
    processAcceptFiles();
  },
  [
    // set by useState
    files
  ]
);

const { getRootProps, getInputProps } = useDropzone({
  onDropAccepted,
  onDropRejected
});

My use case is that, after user drops files into the react dropzone, he will click upload button. said there is something wrong with the upload process. isUploadError will be set.

I want to clean up the files state by using setFiles([]), but files in const isValidBucketLimit = checkBucketLimitSingle(files) are not reflected right after setFiles([])

My question is that is it a way to make files (the state) set back to [], after setFiles([])? If it is still unclear, I will try to explain a bit more.

Upvotes: 1

Views: 2622

Answers (1)

Praud
Praud

Reputation: 297

setFiles is an asynchronous operation, definetely it will not change immediately in a procedure way.

const onDropAccepted = useCallback(
  acceptedFiles => {
    // async
    const processAcceptFiles = async () => {
      let singleFile;

      singleFile = acceptedFiles[0];

      if (acceptedFiles.length > 1) {
        setIsMaxFileNum(true);
        return;
      } else {
        let updatedFiles = [...files];
        // e.g. api upload error
        if (isUploadError) {
          // NOTE: upload error, we empty file buckets
          // But "files" as dependency passed into useCallback, but not reflected after setFiles([])
          updatedFiles = [];
        }

        // check bucket limit
        const isValidBucketLimit = checkBucketLimitSingle(updatedFiles);

        return isValidBucketLimit
          ? setFiles([...updatedFiles, singleFile]);
          : setFiles([])
      }
    };

    // fire
    processAcceptFiles();
  },
  [
    // set by useState
    files
  ]
);

const { getRootProps, getInputProps } = useDropzone({
  onDropAccepted,
  onDropRejected
});

Try this, perhaps is not exactly what you wanted, because all stuff is not clear for me how you use it and what you try to do, but at least, it can help with understanding the idea.

Upvotes: 1

Related Questions