testupload / hooks /useFileUpload.ts
Twan07's picture
Update hooks/useFileUpload.ts
422e938 verified
import { useState, useCallback } from "react";
/* ================= TYPES ================= */
export interface UploadItem {
id: string;
file: File;
progress: number; // 0 → 100
status: "idle" | "uploading" | "done" | "error";
url?: string;
error?: string;
}
/* ================= HOOK ================= */
export function useFileUpload() {
const [uploads, setUploads] = useState<UploadItem[]>([]);
/* ---------- ADD FILE ---------- */
const addFiles = useCallback((files: FileList | File[]) => {
const list = Array.from(files).map((file) => ({
id: crypto.randomUUID(),
file,
progress: 0,
status: "idle" as const,
}));
setUploads((prev) => [...prev, ...list]);
}, []);
/* ---------- UPLOAD SINGLE FILE ---------- */
const uploadFile = useCallback(async (item: UploadItem) => {
const form = new FormData();
form.append("file", item.file);
setUploads((prev) =>
prev.map((u) =>
u.id === item.id
? { ...u, status: "uploading", progress: 0 }
: u
)
);
try {
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);
setUploads((prev) =>
prev.map((u) =>
u.id === item.id ? { ...u, progress: percent } : u
)
);
};
const result: any = await new Promise((resolve, reject) => {
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error("Upload failed"));
}
};
xhr.onerror = () => reject(new Error("Network error"));
xhr.send(form);
});
setUploads((prev) =>
prev.map((u) =>
u.id === item.id
? {
...u,
status: "done",
progress: 100,
url: result.url,
}
: u
)
);
} catch (err: any) {
setUploads((prev) =>
prev.map((u) =>
u.id === item.id
? {
...u,
status: "error",
error: err.message || "Upload error",
}
: u
)
);
}
}, []);
/* ---------- UPLOAD ALL ---------- */
const uploadAll = useCallback(async () => {
for (const item of uploads) {
if (item.status === "idle") {
await uploadFile(item);
}
}
}, [uploads, uploadFile]);
/* ---------- REMOVE FILE ---------- */
const remove = useCallback((id: string) => {
setUploads((prev) => prev.filter((u) => u.id !== id));
}, []);
/* ---------- RESET ---------- */
const reset = useCallback(() => {
setUploads([]);
}, []);
return {
uploads,
addFiles,
uploadFile,
uploadAll,
remove,
reset,
};
}