1. 12:44 10/10/2026
**1. Giới thiệu**
Khi phát triển các ứng dụng sử dụng React cả trên web lẫn di động, việc viết một quy trình upload file riêng biệt cho từng nền tảng sẽ dẫn đến mã nguồn trùng lặp, khó bảo trì và dễ phát sinh lỗi. Bằng cách xây dựng một quy trình có thể tái sử dụng, bạn có thể đảm bảo trải nghiệm người dùng thống nhất và giảm thời gian phát triển.
**2. Tại sao cần một quy trình upload có thể tái sử dụng**
- **Tính nhất quán**: Người dùng thấy giao diện và hành vi giống nhau trên mọi thiết bị.
- **Bảo trì**: Sửa lỗi hoặc thêm tính năng chỉ cần thực hiện một lần.
- **Tận dụng mã nguồn**: Chia sẻ logic business giữa web và native thông qua JavaScript.
- **Kiểm thử dễ dàng**: Một bộ kiểm thử duy nhất bao phủ cả hai nền tảng.
**3. Các thành phần chính**
*React Query (TanStack Query)*
- Quản lý trạng thái upload trên server: trạng thái đang chờ, đã hoàn thành, thất bại.
- Tự động làm mới, hủy yêu cầu và đồng bộ dữ liệu giữa các tab hoặc ứng dụng.
- Cung cấp API đơn giản (`useMutation`) để gửi tệp.
*Context API*
- Lưu trữ các giá trị toàn cục như URL endpoint upload, danh sách chấp nhận tệp, thông tin người dùng hiện tại.
- Giúp các custom hook dễ dàng truy cập các cấu hình mà không cần truyền nhiều props.
*Custom hooks*
- Bao bọc React Query và Context API để cung cấp giao diện người dùng sẵn sàng sử dụng: thanh tiến trình, thông báo lỗi, nút hủy.
- Tách biệt logic nền (upload) khỏi component hiển thị.
**4. Thiết kế quy trình**
- **Tạo một provider** (`UploadProvider`) bao bọc ứng dụng và cung cấp cấu hình qua Context.
- **Xây dựng `useUpload` hook**:
1. Sử dụng `useMutation` từ React Query để gọi API upload.
2. Theo dõi tiến trình (nếu API hỗ trợ `onProgress`).
3. Cung cấp các hàm helper: `uploadFile`, `cancelUpload`, `retryUpload`.
- **Tạo các component** (`UploadButton`, `ProgressBar`, `ErrorMessage`) sử dụng hook trên.
- **Đồng bộ giữa web và native**: Sử dụng cùng một hook; chỉ khác nhau ở phần render (React Native có thể sử dụng `TouchableOpacity` thay vì HTML button).
**5. Ví dụ thực tế**
```javascript
// uploadConfig.js
export const uploadConfig = {
endpoint: 'https://api.example.com/upload',
acceptedTypes: ['image/*', 'application/pdf'],
maxSizeMB: 10,
};
```
```javascript
// useUpload.js
custom hook
import { useMutation } from '@tanstack/react-query';
import { uploadConfig } from './uploadConfig';
export const useUpload = () => {
const mutation = useMutation({
mutationFn: async (file) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(uploadConfig.endpoint, {
method: 'POST',
body: formData,
});
if (!response.ok) throw new Error('Upload thất bại');
return response.json();
},
});
return {
upload: mutation.mutateAsync,
status: mutation.status,
error: mutation.error,
progress: 0, // có thể mở rộng bằng cách lắng nghe event từ fetch
};
};
```
**6. Lợi ích mang lại**
- **Hiệu quả**: Viết một lần, sử dụng cho cả web và native.
- **Mở rộng**: Dễ dàng thêm các tính năng như crop, resize, hoặc tích hợp cloud storage.
- **Kiểm thử**: Các custom hook có thể được kiểm thử riêng biệt với React Query và Context API.
**7. Kết luận**
Bằng cách kết hợp React Query, Context API và custom hooks, bạn có thể xây dựng một quy trình upload file hiện đại, có thể tái sử dụng, hoạt động trơn tru trên React Web và React Native. Cách tiếp cận này không chỉ giúp mã nguồn sạch hơn mà còn rút ngắn thời gian triển khai và tạo nền tảng vững chắc cho các tính năng upload phức tạp hơn trong tương lai.