In this small code that I've written I have created a dynamic object upon setting state and adding files to it. However, the new file overwrites the previous file. With no luck, I have tried using the spread operator inside the array brackets next to mappedFiles
. It seems upon setting the state dynamically, I am not able to concatenate or push to the array inside the files object
.
Here is the code...
import React, { useCallback, useState, useContext } from "react";
import { ImageUploadContext } from "../../Store";
import styles from "./commentssection.module.css";
import { useDropzone } from "react-dropzone";
function ImageUploader({ title }) {
const [files, setFiles] = useContext(ImageUploadContext);
const maxSize = 5048576;
//ISSUE IS HERE. CREATING THE SETSTATE INSIDE THIS CALLBACK
const onDrop = useCallback(
(acceptedFiles) => {
const mappedFiles = acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
);
// This setstate function adds to dynamic array but doesn't return previous one. The array is being overwritten
setFiles((state) => ({ ...state, [title]: [mappedFiles] }));
},
[files]
);
const {
isDragActive,
getRootProps,
getInputProps,
isDragReject,
acceptedFiles,
rejectedFiles,
} = useDropzone({
onDrop,
accept: "image/*",
minSize: 0,
maxSize: 10000000,
});
console.log(files);
const isFileTooLarge = rejectedFiles
? rejectedFiles.length > 0 && rejectedFiles[0].size > maxSize
: null;
return (
<div>
<p>Please include comments in notes</p>
<hr className={styles["HR"]} />
<form className={styles["UploadForm"]}>
<div className={styles["UploadWrapper"]}>
<h5>Upload photos of issues found in the {title}</h5>
<section className={styles["Container"]}>
<div className={styles["ImageInput"]} {...getRootProps()}>
<input {...getInputProps()} />
{!isDragActive && "Click here or drop a file to upload!"}
{isDragActive && !isDragReject && "Drop it like it's hot!"}
{isDragReject && "File type not accepted, sorry!"}
{isFileTooLarge && (
<div className="text-danger mt-2">File is too large.</div>
)}
</div>
</section>
<div>
{files[title]
? files[title].map((object, index) =>
object.map((subObject, subIndex) => {
return (
<img
style={{ height: "80px" }}
className={styles["RenderedImage"]}
key={index}
src={subObject.preview}
/>
);
})
)
: null}
</div>
<p>
Please take a picture of any issues that you find and upload them
here. NOTE: it is only necessary to upload pictures of problems that
you find.
</p>
</div>
<div className={styles["CommentWrapper"]}>
<h5>Notes of the {title}</h5>
<textarea className={styles["Textarea"]} />
</div>
</form>
</div>
);
}
export default ImageUploader;
Edit:
I was able to figure it out thanks to @lawrence-witt1 . Here is the code for the arrays to be parent component specific.
const onDrop = useCallback(
(acceptedFiles) => {
const mappedFiles = acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
);
return files[title]
? setFiles((state) => ({
...state,
[title]: [...state[title], mappedFiles],
}))
: setFiles((state) => ({
...state,
[title]: [mappedFiles],
}));
},
[files, title]
);
I think I spotted the issue - you need to include title
in the dependency array of onDrop
:
const onDrop = useCallback(
(acceptedFiles) => {
...
},
[files, title]
);
Otherwise you run the risk of having a stale value for title
which would overwrite your state object's property.
Edit:
I think this is what you were looking for:
setFiles((state) => ({ ...state, [title]: [...state[title], ...mappedFiles]}));