Avatar

Huy Pham

31/03/2022|4 phút đọc
0
0

Kéo thả và preview image file input trong ReactJS

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.

drop.png

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.

drop1.png

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.

drop2.png

Khi hover.

drop3.png

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.

drop4.png

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.

0Bình luận

Bài viết liên quan

author

Huy Pham

Software Engineer

Insanity is doing the same thing, over and over again, but expecting different results.

Các thẻ đề xuất