Trong các trang web hiện nay, chắc các bạn đã không ít lần sử dụng chức kéo thả image để upload và preview image từ input file. Trong bài viết này, các bạn hãy cùng mình xây dựng chức năng trên bằng react hooks.
Các bước thực hiện:
Bước 1: Mình sẽ xây dựng UI cho component của chúng ta với Tailwind CSS.
import React from "react";
const DropImageInput = () => {
const [file, setFile] = useState()
const inputFileRef = useRef(null)
const [blob, setBlob] = useState('')
const [isDragEnter, setIsDragEnter] = useState(false)
return (
<div
onClick={() => inputFileRef.current && inputFileRef.current.click()}
className={`relative p-6 cursor-pointer h-[200px] w-[570px] mx-auto mt-10 border-2 border-dashed border-blue-600 flex flex-col items-center text-base leading-[1.6] select-none`}
>
<input ref={inputFileRef} type="file" accept="image/*" hidden />
<p className="text-center my-3 pointer-events-none">
Thêm một ảnh đại diện hấp dẫn sẽ giúp bài viết của bạn cuốn hút hơn với
độc giả.
</p>
<p className="text-center text-[#F05123] pointer-events-none">
Kéo thả ảnh vào đây, hoặc bấm để chọn ảnh
</p>
</div>
);
};
export default DropImageInput;
Đây là giao diện của chúng ta.
Bước 2: Mình sẽ viết hàm xử lý file change và dùng hook useEffect để tạo blob URL.
useEffect(() => {
if (file) {
setBlob(URL.createObjectURL(file));
}
return () => {
URL.revokeObjectURL(blob);
};
}, [file]);
const onFileChange = (e) => {
const newFile = e.files?.[0];
if (newFile) {
if (!newFile.type.match("image.*")) {
//File không đúng định dạng
} else {
inputFileRef.current && (inputFileRef.current.value = null);
setFile(newFile);
}
}
};
Bước 3: Hiển thị preview image từ blob URL.
import React, { useEffect, useRef, useState } from "react";
const DropImageInput = () => {
const [file, setFile] = useState();
const [blob, setBlob] = useState("");
const inputFileRef = useRef(null);
const [isDragEnter, setIsDragEnter] = useState(false)
useEffect(() => {
if (file) {
setBlob(URL.createObjectURL(file));
}
return () => {
URL.revokeObjectURL(blob);
};
}, [file]);
const onFileChange = (e) => {
const newFile = e.target.files[0];
if (newFile) {
if (!newFile.type.match("image.*")) {
//File không đúng định dạng
} else {
inputFileRef.current && (inputFileRef.current.value = null);
setFile(newFile);
}
}
};
return (
<div
style={{
"--bg": `url(${blob})`,
}}
onClick={() => inputFileRef.current && inputFileRef.current.click()}
className={`${
blob ? "before-bg-file" : ""
} relative p-6 cursor-pointer h-[200px] w-[570px] mx-auto mt-10 flex flex-col items-center border-2 border-dashed border-blue-600 text-base leading-[1.6] select-none`}
>
<input
ref={inputFileRef}
onChange={onFileChange}
type="file"
accept="image/*"
hidden
/>
<p className="text-center my-3 pointer-events-none">
Thêm một ảnh đại diện hấp dẫn sẽ giúp bài viết của bạn cuốn hút hơn với
độc giả.
</p>
<p className="text-center text-[#F05123] pointer-events-none">
Kéo thả ảnh vào đây, hoặc bấm để chọn ảnh
</p>
</div>
);
};
export default DropImageInput;
Tại file index.css mình thêm một ít css.
.before-bg-file:before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: var(--bg);
background-size: cover;
background-position: 50%;
background-repeat: no-repeat;
opacity: 1;
transition: opacity 0.3s;
}
.before-bg-file:hover:before {
opacity: 0.1;
}
Đây là image preview sau khi ta chọn từ input file.
Khi hover.
Bước 4: Xây dựng các hàm drag and drop.
const onDragLeave = () => {
setIsDragEnter(false);
};
const onDragEnter = () => {
setIsDragEnter(true);
};
const onDrop = (e) => {
setIsDragEnter(false);
const newFile = e.dataTransfer.files?.[0];
if (newFile) {
if (!newFile.type.match("image.*")) {
//File không đúng định dạng
} else {
setFile(newFile);
}
}
};
useEffect(() => {
const handler = (e) => {
e.preventDefault(); // Disable open image in new tab
};
window.addEventListener("dragover", handler);
window.addEventListener("drop", handler);
return () => {
window.removeEventListener("dragover", handler);
window.removeEventListener("drop", handler);
};
}, []);
Đây là toàn bộ ứng dụng của chúng ta.
import React, { useEffect, useRef, useState } from "react";
const DropImageInput = () => {
const [file, setFile] = useState();
const [blob, setBlob] = useState("");
const inputFileRef = useRef(null);
const [isDragEnter, setIsDragEnter] = useState(false);
useEffect(() => {
if (file) {
setBlob(URL.createObjectURL(file));
}
return () => {
URL.revokeObjectURL(blob);
};
}, [file]);
const onFileChange = (e) => {
const newFile = e.target.files[0];
if (newFile) {
if (!newFile.type.match("image.*")) {
//File không đúng định dạng
} else {
inputFileRef.current && (inputFileRef.current.value = null);
setFile(newFile);
}
}
};
const onDragLeave = () => {
setIsDragEnter(false);
};
const onDragEnter = () => {
setIsDragEnter(true);
};
const onDrop = (e) => {
setIsDragEnter(false);
const newFile = e.dataTransfer.files?.[0];
if (newFile) {
if (!newFile.type.match("image.*")) {
//File không đúng định dạng
} else {
setFile(newFile);
}
}
};
useEffect(() => {
const handler = (e) => {
e.preventDefault(); // Disable open image in new tab
};
window.addEventListener("dragover", handler);
window.addEventListener("drop", handler);
return () => {
window.removeEventListener("dragover", handler);
window.removeEventListener("drop", handler);
};
}, []);
return (
<div
style={{
"--bg": `url(${blob})`,
}}
onDrop={onDrop}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onClick={() => inputFileRef.current && inputFileRef.current.click()}
className={`${
blob ? "before-bg-file" : ""
} relative p-6 cursor-pointer h-[200px] w-[570px] mx-auto mt-10 flex flex-col items-center border-2 border-dashed border-blue-600 text-base leading-[1.6] select-none`}
>
<input
ref={inputFileRef}
onChange={onFileChange}
type="file"
accept="image/*"
hidden
/>
<p className="text-center my-3 pointer-events-none">
Thêm một ảnh đại diện hấp dẫn sẽ giúp bài viết của bạn cuốn hút hơn với
độc giả.
</p>
<p className="text-center text-[#F05123] pointer-events-none">
{isDragEnter
? "Thả ảnh vào đây"
: "Kéo thả ảnh vào đây, hoặc bấm để chọn ảnh"}
</p>
</div>
);
};
export default DropImageInput;
Đây là khi Drag Enter.
Tạm kết
Trên đây là cách để xây dựng component kéo thả và preview image file input. Hy vọng sẽ phần nào giúp ích được cho các bạn. Cảm ơn các bạn đã đọc bài viết của mình <3.