I have developed a upload functionality, upload component inside accordion. Now I want to retain the data/files that were uploaded in the system when the accordion is collapsed and expanded.
Now I am able to retain the files, but deleting them is throwing the error.
Error:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Design
CODE
/** set the state locally from parent state
* component did mount
*/
useEffect(() => {
if (parentState[`${field}acceptedFiles`]) {
setAcceptedFiles([...parentState[`${field}acceptedFiles`]]);
}
if (parentState[`${field}rejectedFiles`]) {
console.log('REJECTED FILES', parentState[`${field}rejectedFiles`]);
setRejectedFiles([...parentState[`${field}rejectedFiles`]]);
}
}, []);
/** maintain the state in parent state
* this is used retain the state from parent state
*/
useEffect(() => {
const afile = {
[`${field}acceptedFiles`]: acceptedFiles
};
onChange(afile);
}, [acceptedFiles]);
useEffect(() => {
const rfile = {
[`${field}rejectedFiles`]: rejectedFiles
};
onChange(rfile);
}, [rejectedFiles]);
/** deleting the rejected files, removing them from UI */
const deleteRejectedFile = (uuid) => {
setRejectedFiles((updatedRejectedFiles) => {
return updatedRejectedFiles.filter(rejectedFile => rejectedFile.uuid !== uuid);
});
}
/** deleting the accepted files, removing them from UI and removing from attachments
* Send back uploadAttachments to parent state */
const deleteAcceptedFile = (uuid) => {
setAcceptedFiles((updatedAcceptedFiles) => {
return updatedAcceptedFiles.filter(acceptedFile => acceptedFile.uuid !== uuid);
});
}
const handleRejectedFiles = (rejected, docTypeError) => {
const files = rejected.map(file => {
const uuid = getUUID();
return {
name: file.name,
uuid,
rightIconItems: {
onIconClick: () => deleteRejectedFile(uuid)
},
}});
setRejectedFiles([...rejectedFiles, ...files]);
}
const handleAcceptedFiles = (accepted) => {
const files = accepted.map((file) => {
const uuid = getUUID();
const { PENDING, INFO } = constants;
return {
...file,
file: file,
uuid,
rightIconItems: {
onIconClick: () => deleteAcceptedFile(uuid)
}
}
})
setAcceptedFiles([...acceptedFiles, ...files]);
}
const onFileSelected = (accepted, rejected) => {
handleAcceptedFiles(accepted);
handleRejectedFiles(rejected);
};
return (
<DragAndDropContainer>
<FileSelector
onFileSelected={onFileSelected}
acceptedFiles={acceptedFiles}
rejectedFiles={rejectedFiles}
/>
</DragAndDropContainer>
);
Updated the Error
The error is in the delete function, as accordion is closed the component is unmounted, but setState
is still pointing to its old reference [scope].
Is there a way to point the setstate
to newly created component, instead of pointing to unmounted component without moving the state to parent function.
/** deleting the rejected files, removing them from UI */
const deleteRejectedFile = (uuid) => {
setRejectedFiles((updatedRejectedFiles) => {
return updatedRejectedFiles.filter(rejectedFile => rejectedFile.uuid !== uuid);
});
}
This error was caused due to referencing of the delete method. deleteRejectedFile
and deleteAcceptedFile
function's setstate
methods were pointing to the component which was unmounted instead of pointing to the mounted component.
So I have created a new reference of the icon
useEffect(() => {
if (parentState[`${field}acceptedFiles`]) {
const newAcceptedReferences = parentState[`${field}acceptedFiles`].map((accepted) => {
const rightIconItems = {
onIconClick: () => deleteAcceptedFile(accepted.uuid)
};
return {
...accepted,
rightIconItems
}
});
setAcceptedFiles(newAcceptedReferences);
}
if (parentState[`${field}rejectedFiles`]) {
const newRejectedReferences = parentState[`${field}rejectedFiles`].map((rejected) => {
const rightIconItems = {
onIconClick: () => deleteRejectedFile(rejected.uuid)
};
return {
...rejected,
rightIconItems
}
});
setRejectedFiles(newRejectedReferences);
}
}, []);