javascriptreactjsdrop

Uploading a file with drop


I need to drag a file and collect its information: get its name and pass its content to Base64. When uploading the document doing the drop I get an error "Cannot read properties of undefined (reading 'type')" when I get to the if(event.dataTransfer.files[0].type !== "application/pdf") line. Somehow I need to wait for it to load the document to handle the information. If I put a condition to enter if it receives a file, it skips the if and exits the function. Attached is the simplified code.

import React, {useState} from "react";
import {
    Box,
    Button,
    Typography
} from "@material-ui/core";
import data from "./data.json"
import UploadFileIcon from "@mui/icons-material/UploadFile";

const Up = ({}) => {
    const [popUpConfirm, setPopUpConfirm] = useState(false);
    const [selectedFile, setSelectedFile] = useState();
    const [isFilePicked, setIsFilePicked] = useState(false);
    const [isLoading, setIsLoading] = useState(false);

    const handleSubmit = (event) => {
        event.preventDefault();
        setPopUpConfirm(true);
    };

    const changeHandler = (event) => {
        //changeHandler works
    };

    const handleDragOver = (event) => {
        event.preventDefault();
        event.stopPropagation();
    };

    const handleDrop = (event) => {
        event.preventDefault();
        setIsLoading(true);
        if(event.dataTransfer.files[0].type !== "application/pdf") {
            alert("Must be PDF.");
            return;
        }
        if(event.target.files) {
            setSelectedFile(event.dataTransfer.files[0]);
            setIsFilePicked(true);
            data.documentName = event.dataTransfer.files[0].name;
            getBase64(event.dataTransfer.files[0]).then(
                doc => data.document = doc
            );
        }
        setIsLoading(false);
    };

    //extracts the base64 content (removes the metadata at the beginning) and ensures padding is done correctly.
    function getBase64(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = () => {
                let encoded = reader.result.toString().replace(/^data:(.*,)?/, '');
                if ((encoded.length % 4) > 0) {
                    encoded += '='.repeat(4 - (encoded.length % 4));
                }
                resolve(encoded);
            };
            reader.onerror = error => reject(error);
        });
    }

    return (
        <>
            <form onSubmit={handleSubmit}>
                <Box onDragOver={handleDragOver} onDrop={handleDrop}>
                    <Box>
                        <Button >
                            {" "}
                            <UploadFileIcon/> Choose a document
                            <input type="file" name="file" onChange={changeHandler} hidden/>
                        </Button>
                        <Typography>Or drop your document here</Typography>
                        {isFilePicked ?
                            <Typography>
                                {selectedFile.name}
                            </Typography>
                            : null}
                    </Box>
                </Box>
                <Box>
                    <Button type="submit">
                        UPLOAD
                    </Button>
                </Box>
            </form>
            <UploadFile open={popUpConfirm} setOpen={setPopUpConfirm} data={data}/>  
        </>
    );
}

export default Up;

Solution

  • In event.dataTransfer, files property is a FileList object, which does not have a type property, and items property is a DataTransferItemList object, which has a type property that returns the MIME type of the file.

    So you need to change your code to use event.dataTransfer.items[0].type instead of event.dataTransfer.files[0].type in handleDrop method.

    And you need to make sure that the event has a dataTransfer, and that the dataTransfer property has a files, and that the files is not an empty array.

    For example:

    const handleDrop = (event) => {
      event.preventDefault();
      setIsLoading(true);
      const file = event.dataTransfer?.files?.[0] ?? null;
      if (file) {
        if (file.type !== "application/pdf") {
          alert("Must be PDF.");
          return;
        }
        setSelectedFile(file);
        setIsFilePicked(true);
        data.documentName = file.name;
        getBase64(file).then((doc) => (data.document = doc));
      }
      setIsLoading(false);
    };