fix: clear upload progress interval on unmount

Store the progress setInterval ID in a ref and clear it in a
useEffect cleanup. Previously the interval was stored in a local
variable and only cleared in onSuccess/onError callbacks, leaking
if the component unmounted mid-upload.

Fixes #1090
This commit is contained in:
Matt Van Horn 2026-04-03 00:05:06 -07:00
parent c1c4c534c0
commit 134beec392

View file

@ -4,7 +4,7 @@ import { useAtom } from "jotai";
import { CheckCircle2, FileType, FolderOpen, Info, Upload, X } from "lucide-react";
import { useTranslations } from "next-intl";
import { type ChangeEvent, useCallback, useMemo, useRef, useState } from "react";
import { type ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useDropzone } from "react-dropzone";
import { toast } from "sonner";
import { uploadDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
@ -132,6 +132,15 @@ export function DocumentUploadTab({
const { mutate: uploadDocuments, isPending: isUploading } = uploadDocumentMutation;
const fileInputRef = useRef<HTMLInputElement>(null);
const folderInputRef = useRef<HTMLInputElement>(null);
const progressIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => {
return () => {
if (progressIntervalRef.current) {
clearInterval(progressIntervalRef.current);
}
};
}, []);
const acceptedFileTypes = useMemo(() => {
const etlService = process.env.NEXT_PUBLIC_ETL_SERVICE;
@ -236,7 +245,7 @@ export function DocumentUploadTab({
setUploadProgress(0);
trackDocumentUploadStarted(Number(searchSpaceId), files.length, totalFileSize);
const progressInterval = setInterval(() => {
progressIntervalRef.current = setInterval(() => {
setUploadProgress((prev) => (prev >= 90 ? prev : prev + Math.random() * 10));
}, 200);
@ -249,14 +258,14 @@ export function DocumentUploadTab({
},
{
onSuccess: () => {
clearInterval(progressInterval);
if (progressIntervalRef.current) clearInterval(progressIntervalRef.current);
setUploadProgress(100);
trackDocumentUploadSuccess(Number(searchSpaceId), files.length);
toast(t("upload_initiated"), { description: t("upload_initiated_desc") });
onSuccess?.();
},
onError: (error: unknown) => {
clearInterval(progressInterval);
if (progressIntervalRef.current) clearInterval(progressIntervalRef.current);
setUploadProgress(0);
const message = error instanceof Error ? error.message : "Upload failed";
trackDocumentUploadFailure(Number(searchSpaceId), message);