testupload / frontend /hooks /useFileUpload.ts
Twan07's picture
Update frontend/hooks/useFileUpload.ts
a34c56d verified
import { useCallback, useState } from "react";
import { FileItem, UploadStatus } from "../types";
export function useFileUpload() {
const [files, setFiles] = useState<FileItem[]>([]);
const [isUploading, setIsUploading] = useState(false);
// ✅ App.tsx + FileUploader truyền FileItem[]
const addFiles = useCallback((items: FileItem[]) => {
setFiles((prev) => [...prev, ...items]);
}, []);
const removeFile = useCallback((id: string) => {
setFiles((prev) => prev.filter((f) => f.id !== id));
}, []);
const updateFilePath = useCallback((id: string, path: string) => {
setFiles((prev) =>
prev.map((f) => (f.id === id ? { ...f, path } : f))
);
}, []);
const uploadOne = useCallback((item: FileItem) => {
return new Promise<void>((resolve, reject) => {
const form = new FormData();
form.append("file", item.file);
form.append("path", item.path);
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/upload");
xhr.upload.onprogress = (e) => {
if (!e.lengthComputable) return;
const percent = Math.round((e.loaded / e.total) * 100);
setFiles((prev) =>
prev.map((f) =>
f.id === item.id
? {
...f,
progress: percent,
status: UploadStatus.UPLOADING,
}
: f
)
);
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
const res = JSON.parse(xhr.responseText);
setFiles((prev) =>
prev.map((f) =>
f.id === item.id
? {
...f,
progress: 100,
status: UploadStatus.SUCCESS,
url: res.url,
}
: f
)
);
resolve();
} else {
setFiles((prev) =>
prev.map((f) =>
f.id === item.id
? { ...f, status: UploadStatus.ERROR }
: f
)
);
reject(new Error("Upload failed"));
}
};
xhr.onerror = () => {
setFiles((prev) =>
prev.map((f) =>
f.id === item.id
? { ...f, status: UploadStatus.ERROR }
: f
)
);
reject(new Error("Network error"));
};
xhr.send(form);
});
}, []);
const startUpload = useCallback(async () => {
if (isUploading) return;
setIsUploading(true);
try {
for (const f of files) {
if (
f.status === UploadStatus.IDLE ||
f.status === UploadStatus.ERROR
) {
await uploadOne(f);
}
}
} finally {
setIsUploading(false);
}
}, [files, uploadOne, isUploading]);
return {
files,
isUploading,
addFiles,
removeFile,
updateFilePath,
startUpload,
};
}