I need to return ALL files dropped into the dropzone as an array of parsed JSONs and use that array as the "source of truth" in the parent component one level above. When the files are loaded, I read them with the FileReader
, parse them, and create a payload, so that all the needed data parsed from files is sent to the parent component as a JSON payload, from where it can be redistributed further as props.
I need this parsed data to be an array of JSONs, but when I try to create one I either get Object is not iterable
error or an array of undefined
.
Dropzone component (which I installed from here)
const Dropzone = ({
onSetPayload,
onResetPayload,
onAddCommonName,
...props
}) => {
const onDrop = useCallback(
(acceptedFiles) => {
onResetPayload([]);
acceptedFiles.forEach((file) => {
const reader = new FileReader();
reader.onabort = () => {
throw new Error("file reading was aborted");
};
reader.onerror = () => {
throw new Error("file reading has failed");
};
reader.onload = () => {
const cert = new x509.X509Certificate(reader.result);
const payload = getPayload(cert); // parses my certificate into a JSON payload
onSetPayload(payload);
};
reader.readAsArrayBuffer(file);
});
},
[onSetPayload, onResetPayload]
);
The solution I've got so far is to update state based on previous state - i.e. a batch of files is dropped, then they are read and parsed one-by-one and our setPayload
re-sets its state as many times as there are files dropped, which I want to avoid.
Parent component (CardContent).
const CardContent = (props) => {
const [addIsActive, setAddIsActive] = useState(true);
const toggleAddButton = () => setAddIsActive((prevState) => !prevState);
const [payload, setPayload] = useState([]);
const getPayload = (p) => setPayload((prev) => [...prev, p]);
/* This setPayload does the job of a vanilla reducer, which is suboptimal.
It updates state as many times as there are files coming from FileReader.
I would rather overwrite state once with a new array from FileReader. */
return (
<section className={styles.CardContent}>
<Aside
onAddClick={toggleAddButton}
addIsActive={addIsActive}
payload={payload}
/>
{addIsActive ? (
<Dropzone
onSetPayload={getPayload}
onResetPayload={setPayload}
/>
) : (
<output style={{ border: "2px solid #333" }}>This is output</output>
)}
</section>
);
};
I cannot create an array and mutate it. I cannot seem to spread the results of reader.onload
calls into an array. When I try to map
the acceptedFiles into a new array, undefined is returned or worse - an infinite loop ensues.
Thank you in advance for any help.
You could try wrapping your Filereader code in a promise. Collect the array of promises, run it through Promise.all, setstate in .then func.
(acceptedFiles) => {
onResetPayload([]);
const promises = acceptedFiles.map(file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onabort = () => {
reject("file reading was aborted");
};
reader.onerror = () => {
reject("file reading has failed");
};
reader.onload = () => {
const cert = new x509.X509Certificate(reader.result);
const payload = getPayload(cert); // parses my certificate into a JSON payload
resolve(payload);
};
reader.readAsArrayBuffer(file);
}));
Promise.all(promises).then(resultArr => {
// resultArr is an array of all the promise results. It should be the array you are after
setToSomeState(resultArr);
},
error => {
// do something with error
});