Search code examples
javascriptreactjstypescriptes6-promisereact-dropzone

Asynchronous validation on uploaded file (React Dropzone + TS)


I need to validate the duration of the uploaded video using React Dropzone. The problem is every method I have found is async, while the validator function of the library should be synchronous. How can I run async code within the synchronous function?

Getting video Duration function:

async function getVideoDuration(videoFile: File | Blob) {
  return new Promise<number>((res, _) => {
    const video = document.createElement("video")
    video.preload = "metadata"
    video.onloadeddata = function () {
      res(video.duration)
    }
    video.src = URL.createObjectURL(videoFile)
  })
}

My React Zone validator function:

    validator(f) {
      let file: (File) | null
      if (f instanceof DataTransferItem) {
        file = f.getAsFile()
      } else file = f
      if (!file) return { message: "File Expected", code: "IvalidDragObject" }
      console.log(file)

      if (file.size > 5e8) return { message: "File too large (max 500 MB)", code: "FileTooLarge" }

      if (!file) return { message: "File Expected", code: "IvalidDragObject" }
const duration = await getVideoDuration(file) // 'await' expressions are only allowed within async functions and at the top levels of modules.

      if (duration > 15 * 60) {
        return { message: "File Duration should be less than 15 minutes", code: "LargeFileDuration" }
      }

      return null
    },

Other workarounds were to use the getFilesFromEvent function and pass custom props to the files there, but the events it gives are very different and it is tedious to implement.


Solution

  • I've forked this project here with the changes necessary to support asynchronous validation. Please note that the noDragEventsBubbling parameter is not supported at this point in time. Simply pass in an asynchronous (or synchronous) validation callback function, as seen below, and use as originally intended.

    import { useDropzone } from 'react-dropzone'
    
    function MyDropzone() {
      const onDrop = useCallback(acceptedFiles => {
        // Do something with the files
      }, [])
      const { getRootProps, getInputProps, isDragActive } = useDropzone({
        onDrop,
        validator: async (file) => {
          // do validation here
        }
      })
    
      return (
        <div {...getRootProps()}>
          <input {...getInputProps()} />
          {
            isDragActive ?
              <p>Drop the files here ...</p> :
              <p>Drag 'n' drop some files here, or click to select files</p>
          }
        </div>
      )
    }