У меня есть компонент React перетаскивания, написанный на чистом HTML. Компонент позволяет пользователям перетаскивать один или несколько файлов. Как только файлы помещаются в зону перетаскивания, имена файлов отображаются под зоной перетаскивания вместе с крестообразной кнопкой «x» для удаления любого из перетаскиваемых файлов, как показано на снимке экрана ниже:
Этот компонент отображает только уникальные файлы. Это означает, что если перетаскивать повторяющиеся файлы, экран отображает эти файлы только один раз. Однако где-то в моем коде дубликаты файлов сохраняются, и здесь проблема в том, что если я удаляю файл из пользовательского интерфейса и предполагаю, что он был удален несколько раз, этот файл будет появляться снова и снова всякий раз, когда я удаляю его из UI. Он продолжает появляться столько раз, сколько я его перетаскивал в dropzone. Это проблема, которую я не могу решить в своем коде.
Я хочу, чтобы этот код был обновлен так, чтобы, если файл удаляется несколько раз, он должен отображаться только по одному (что уже происходит), но также, когда я удаляю этот файл из пользовательского интерфейса, он не должен появляться снова.
Ниже я предоставляю все мои App.js и App.css, которые вы можете легко скопировать и вставить и запустить поведение, если хотите:
App.js:
import React, { useRef, useState, useEffect } from "react";
import "./App.css";
function App() {
const fileInputRef = useRef();
const [selectedFiles, setSelectedFiles] = useState([]);
const [validFiles, setValidFiles] = useState([]);
const [unsupportedFiles, setUnsupportedFiles] = useState([]);
const [errorMessage, setErrorMessage] = useState("");
// useEffect Hook that removes stops duplicate files to be displayed
useEffect(() => {
let filteredArr = selectedFiles.reduce((acc, current) => {
const x = acc.find((item) => item.name === current.name);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
setValidFiles([...filteredArr]);
}, [selectedFiles]);
// fileDrop method that access all the files which are being dragged and sends to handleFiles method
const fileDrop = (e) => {
preventDefault(e);
const files = e.dataTransfer.files;
if (files.length) {
handleFiles(files);
}
};
// fileSelected method that sends the files to handleFiles
const filesSelected = () => {
if (fileInputRef.current.files.length) {
handleFiles(fileInputRef.current.files);
}
};
// handleFiles method that receives all the files being dragged
// validates if the files are of correct type
// sets the selectedFiles state if valid or sets errorMessage state variable
const handleFiles = (files) => {
for (let i = 0; i < files.length; i++) {
if (validateFile(files[i])) {
setSelectedFiles((prevArray) => [...prevArray, files[i]]);
} else {
files[i]["invalid"] = true;
setSelectedFiles((prevArray) => [...prevArray, files[i]]);
setErrorMessage("File type not permitted");
setUnsupportedFiles((prevArray) => [...prevArray, files[i]]);
}
}
};
// method that holds the valid allowed file types and checks if each of the file is valid or not
const validateFile = (file) => {
const validTypes = [
"image/jpeg",
"image/jpg",
"image/png",
"image/gif",
"image/x-icon",
];
if (validTypes.indexOf(file.type) === -1) {
return false;
}
return true;
};
// method that receives the file name and handles the deletion of the file from all state variables.
const removeFile = (name) => {
const index = validFiles.findIndex((e) => e.name === name);
const index2 = selectedFiles.findIndex((e) => e.name === name);
const index3 = unsupportedFiles.findIndex((e) => e.name === name);
validFiles.splice(index, 1);
selectedFiles.splice(index2, 1);
setValidFiles([...validFiles]);
setSelectedFiles([...selectedFiles]);
if (index3 !== -1) {
unsupportedFiles.splice(index3, 1);
setUnsupportedFiles([...unsupportedFiles]);
}
};
// methods to prevent default browser bahavior on dragOver, dragEnter and dragLeave
const preventDefault = (e) => {
e.preventDefault();
};
const dragOver = (e) => {
preventDefault(e);
};
const dragEnter = (e) => {
preventDefault(e);
};
const dragLeave = (e) => {
preventDefault(e);
};
// final HTML returned by the App component
return (
<div>
<p className="title">React Drag and Drop Image Upload</p>
<div className="content">
<>
<div className="container">
<div
className="drop-container"
onDragOver={dragOver}
onDragEnter={dragEnter}
onDragLeave={dragLeave}
onDrop={fileDrop}
>
<div className="drop-message">
Drag & Drop files here or click to select file(s)
</div>
<input
ref={fileInputRef}
className="file-input"
type="file"
multiple
onChange={filesSelected}
/>
</div>
<div className="file-display-container">
{validFiles.map((data, i) => (
<div className="file-status-bar" key={i}>
<div>
<span
className={`file-name ${
data.invalid ? "file-error" : ""
}`}
>
{data.name}
</span>
{data.invalid && (
<span className="file-error-message">
({errorMessage})
</span>
)}
</div>
<div
className="file-remove"
onClick={() => removeFile(data.name)}
>
X
</div>
</div>
))}
</div>
</div>
</>
</div>
</div>
);
}
export default App;
App.css:
.title {
font-size: 2rem;
text-align: center !important;
margin-top: 10%;
color: #4aa1f3;
font-weight: bold;
}
.content {
background-color: white;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.file-input {
display: none;
}
.container {
transform: translateY(-100%);
}
.container p {
color: red;
text-align: center;
}
.drop-container {
display: flex;
align-items: center;
justify-content: center;
margin: 0;
width: 800px;
height: 200px;
border: 4px dashed #4aa1f3;
}
.drop-message {
text-align: center;
color: #4aa1f3;
font-family: Arial;
font-size: 20px;
}
.file-display-container {
position: fixed;
width: 805px;
}
.file-status-bar {
width: 100%;
vertical-align: top;
margin-top: 10px;
margin-bottom: 20px;
position: relative;
line-height: 50px;
height: 50px;
}
.file-status-bar>div {
overflow: hidden;
}
.file-name {
display: inline-block;
vertical-align: top;
margin-left: 50px;
color: #4aa1f3;
}
.file-error {
display: inline-block;
vertical-align: top;
margin-left: 50px;
color: #9aa9bb;
}
.file-error-message {
color: red;
}
.file-remove {
position: absolute;
top: 20px;
right: 10px;
line-height: 15px;
cursor: pointer;
color: red;
margin-right: -10px;
}
Большое спасибо за помощь и поддержку.