FileDummy Logo
FileDummy
Tải Lên & Xử Lý Tệp Tin

Tạo Khu Vực Kéo Thả File Trong React Với react-dropzone

Xây dựng component kéo thả file chuyên nghiệp trong React với react-dropzone. Hỗ trợ lọc định dạng, xem trước và hiển thị tiến trình.

29 tháng 10, 20269 phút đọc1,420 lượt xem
reactreact-dropzonedrag-dropfile-uploadfrontend

Xây dựng giao diện tải lên tệp kéo thả mượt mà trong React nâng cao tính khả dụng và sự tương tác. Trong hướng dẫn này, chúng ta xây dựng một component dropzone có thể truy cập, responsive sử dụng react-dropzone với hỗ trợ xem trước tệp đầy đủ và tiến trình tải lên.

Cài Đặt react-dropzone

Terminal
npm install react-dropzone lucide-react
Thử Nghiệm Thực Tế.pdf

Tải file PDF test 10MB chuẩn để kiểm thử component dropzone kéo thả trong React.

Tải File Mẫu 10MB PDF →

Component React Hoàn Chỉnh

TSX
import React, { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { UploadCloud, FileText, CheckCircle2, AlertCircle } from 'lucide-react';

export function FileDropzone() {
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  const onDrop = useCallback((acceptedFiles: File[], fileRejections: any[]) => {
    setErrorMessage(null);

    if (fileRejections.length > 0) {
      const error = fileRejections[0].errors[0];
      setErrorMessage(error.message);
      return;
    }

    if (acceptedFiles.length > 0) {
      setSelectedFile(acceptedFiles[0]);
    }
  }, []);

  const { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
    onDrop,
    maxFiles: 1,
    maxSize: 20 * 1024 * 1024, // 20MB
    accept: {
      'application/pdf': ['.pdf'],
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
      'image/*': ['.png', '.jpg', '.jpeg'],
    },
  });

  return (
    <div className="max-w-xl mx-auto my-8">
      <div
        {...getRootProps()}
        className={`p-8 border-2 border-dashed rounded-2xl text-center cursor-pointer transition-colors ${
          isDragActive
            ? 'border-blue-500 bg-blue-500/10'
            : isDragReject
            ? 'border-red-500 bg-red-500/10'
            : 'border-slate-700 bg-slate-900/50 hover:border-slate-600'
        }`}
      >
        <input {...getInputProps()} />
        <div className="flex flex-col items-center justify-center space-y-3">
          <div className="p-3 bg-blue-500/10 rounded-xl text-blue-400">
            <UploadCloud className="w-8 h-8" />
          </div>
          {isDragActive ? (
            <p className="text-sm font-medium text-blue-400">Thả tệp vào đây...</p>
          ) : (
            <div>
              <p className="text-sm font-semibold text-white">
                Nhấp để tải lên hoặc kéo và thả
              </p>
              <p className="text-xs text-slate-400 mt-1">
                PDF, DOCX, PNG, hoặc JPG (tối đa 20MB)
              </p>
            </div>
          )}
        </div>
      </div>

      {selectedFile && (
        <div className="mt-4 p-4 rounded-xl bg-slate-900 border border-slate-800 flex items-center justify-between">
          <div className="flex items-center space-x-3">
            <FileText className="w-5 h-5 text-blue-400" />
            <div>
              <p className="text-sm font-medium text-white">{selectedFile.name}</p>
              <p className="text-xs text-slate-400">
                {(selectedFile.size / (1024 * 1024)).toFixed(2)} MB
              </p>
            </div>
          </div>
          <CheckCircle2 className="w-5 h-5 text-emerald-400" />
        </div>
      )}

      {errorMessage && (
        <div className="mt-4 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-xs flex items-center space-x-2">
          <AlertCircle className="w-4 h-4 flex-shrink-0" />
          <span>{errorMessage}</span>
        </div>
      )}
    </div>
  );
}

Các Quy Tắc UX Tốt Nhất Cho Dropzone

  • Phản hồi trực quan khi kéo qua: Làm tối nền hoặc làm sáng viền khi tệp hợp lệ đang được hover.
  • Trạng thái từ chối rõ ràng: Hiển thị kiểu màu đỏ riêng biệt nếu định dạng tệp không hỗ trợ được kéo qua.
  • Điều hướng bàn phím: Đảm bảo người dùng dùng bàn phím có thể nhấn Tab để focus vào dropzone và Enter để mở bộ chọn tệp OS gốc.
NDL

Nguyễn Đại Long

Tác Giả

Backend Lead • Chuyên gia Kiến trúc Hệ thống Phân tán & Lưu trữ Đám mây

Hơn 4 năm kinh nghiệm thiết kế các hệ thống xử lý tệp tải lên thông lượng lớn, tối ưu hóa cơ sở dữ liệu và hạ tầng phân tán Cloudflare R2 / AWS S3. Người sáng lập FileDummy và Mạng lưới Hệ sinh thái NDL.

Bài viết này có hữu ích không?

Bấm Thích để ủng hộ tác giả và giúp bài viết lan tỏa tới cộng đồng lập trình viên.

Thảo Luận Kỹ Thuật & Đóng Góp Ý Kiến (0)

Chia sẻ kết quả benchmark, phản hồi các trường hợp biên hoặc đặt câu hỏi chuyên môn.

0/3000

Chưa có bình luận nào. Hãy là lập trình viên đầu tiên bắt đầu cuộc thảo luận!

Câu Hỏi Thường Gặp (FAQ)

What is the difference between react-dropzone and the HTML5 drag-and-drop API?▾

react-dropzone wraps the HTML5 File API with an onDrop callback, MIME type filtering via the accept prop, maxSize validation, and accessible keyboard support — saving 100+ lines of boilerplate compared to raw HTML5 drag events.

How do I limit file types in react-dropzone?▾

Pass the accept prop as an object: { 'application/pdf': ['.pdf'], 'image/*': ['.jpg', '.png'] }. This is the react-dropzone v12+ API (breaking change from the old string format in v11).

Can react-dropzone upload files directly to S3?▾

react-dropzone only provides the File object — it does not handle uploads. Combine it with a presigned S3 PUT request or the tus-js-client library to upload directly from the browser to S3/R2 without a proxy server.

Bài Viết Liên Quan