Tengo un componente de carga de archivos. El comportamiento es simple: envío una solicitud de carga al back-end por archivo y, a medida que aumenta el progreso de la carga, tengo una barra que debería aumentar con él.
Tengo un estado que contiene cada archivo seleccionado y su respectivo progreso, como tal:
interface IFiles { file: File; currentProgress: number; } const [selectedFiles, setSelectedFiles] = useState<IFiles[]>([]); Y cuando el usuario haga clic en el botón de carga, esta función se activará y llamará a uploadFile para cada archivo en mi estado de matriz.
const sendFilesHandler = async () => { selectedFiles.map(async (file) => { const fileType = file.file.type.split('/')[0]; const formData = new FormData(); formData.append(fileType, file.file); formData.append('filename', file.file.name); await uploadFile(formData, updateFileUploadProgress); }); }; Así es como se ve la función uploadFile .
const uploadFile = async (body: FormData, setPercentage: (filename: string, progress: number) => void) => { try { const options = { onUploadProgress: (progressEvent: ProgressEvent) => { const { loaded, total } = progressEvent; const percent = Math.floor((loaded * 100) / total); const fileName = body.get('filename')!.toString(); if (percent <= 100) { setPercentage(fileName, percent) } } }; await axios.post( "https://nestjs-upload.herokuapp.com/", body, options ); } catch (error) { console.log(error); } };Como puede ver, cuando se activa uploadProgress, debería informar a la función setPercentage de la llamada, que es:
const updateFileUploadProgress = (fileName: string, progress: number) => { console.log('Entrada', selectedFiles); const currentObjectIndex = selectedFiles.findIndex((x) => fileName === x.file.name); const newState = [...selectedFiles]; newState[currentObjectIndex] = { ...newState[currentObjectIndex], currentProgress: progress, }; setSelectedFiles(newState); console.log('Saída', newState); };Y esta función solo debería actualizar el objeto de mi matriz de estado donde coinciden los nombres de archivo. Sin embargo, está anulando todo el asunto. El comportamiento es el siguiente:
Entonces parece que todo está bien siempre que actualice el mismo objeto. Pero en el momento en que onUploadProgress se activa en otro objeto, los estados de los archivos selectedFiles vuelven a su estado inicial. ¿Qué me falta para que esto funcione correctamente?
No estoy seguro de cuál es la razón exacta detrás de este comportamiento, pero se me ocurrió la siguiente solución inesperadamente simple después de pasar 3 horas seguidas.
const updateFileUploadProgress = (fileName: string, progress: number) => { setSelectedFiles(prevState => { const currentObjectIndex = prevState.findIndex((x) => fileName === x.file.name); const newState = [...prevState]; newState[currentObjectIndex] = { ...newState[currentObjectIndex], currentProgress: progress, }; return newState; }) };Pude imitar su problema localmente y lo resolví con el enfoque anterior. Creo que fue porque la función (por alguna razón) todavía hacía referencia al estado inicial incluso después de los renderizados.