0

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]
  );
4
  • try doing this setFiles({ ...file, [title]: [mappedFiles] }); Commented Aug 9, 2020 at 20:00
  • Same result as before Commented Aug 9, 2020 at 20:04
  • What does ImageUploadContext look like? Commented Aug 9, 2020 at 20:50
  • It's an empty object. Ex. useState({}) Commented Aug 9, 2020 at 20:59

2 Answers 2

1

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]}));
Sign up to request clarification or add additional context in comments.

8 Comments

That didn't fix it. The issue is whether or not the array state inside the object not being returned along with the new value. Therefore, this setState is overriding the state. The title is consistently returning and read.
You are using a new value for title every time you set the state right?
No, the title works as expected. It's being passed down from the parent.
You can't have two identical keys on the same object. If title does not change then this is the expected behaviour. Perhaps you want files to be an array, and each callback invocation to create a new object in it?
I could do that. However, I would like it to be component specific to where I can access it. If I put it as an array, then any component would have access to it without a specific key. What I mean is I'm trying to have it organized so that each parent that uses this component has a key to a part it can access and render the images in that component. Hopefully, I explained it well...
|
0

If your state object with array looks like below

var std={students:{names:["AAA","BBB","CCC"]}}

State

const[students,setstudents]=useState(std)

setState

 setstudents({ ...students, ...students.names.push("DDD") })

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.