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;
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);
};