Home > Enterprise >  Passing values from child to parent
Passing values from child to parent

Time:10-19

I have built a component that uploads files using react dropzone. In my parent component I would like to send this files to the server. My code looks like this:

Child component:

const UploadMultipleImages = () => {
  const [files, setFiles] = useState([]);

  const { getRootProps, getInputProps } = useDropzone({
    accept: {
      "image/*": [],
    },
    onDrop: (acceptedFiles) => {
      setFiles(
        acceptedFiles.map((file) =>
          Object.assign(file, {
            preview: URL.createObjectURL(file),
          })
        )
      );
    },
  });

  const removeFile = (file) => () => {
    const newFiles = [...files];
    newFiles.splice(newFiles.indexOf(file), 1);
    setFiles(newFiles);
  };

  useEffect(() => {
    return () => files.forEach((file) => URL.revokeObjectURL(file.preview));
  }, []);

  return (
    <section className="image-uploader">
      <div
        className="image-upload"
        {...getRootProps({ className: "dropzone" })}
      >
        <input {...getInputProps()} />
        <div className="image-upload__button">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="30"
            height="30"
            fill="currentColor"
            className="bi bi-plus-square"
            viewBox="0 0 16 16"
            cursor={"pointer"}
          >
            <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z" />
            <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
          </svg>
        </div>
      </div>
      <div className="image-uploader__result">
        {files.map((file) => (
          <div key={file.name}>
            <div className="image-uploader__container">
              <img
                className="image-uploader__result-image"
                src={file.preview}
                onl oad={() => {
                  URL.revokeObjectURL(file.preview);
                }}
                alt="not found"
                width="60px"
                height="40px"
              />
              <div className="image-uploader__overlay"></div>
              <div className="image-uploader__delete">
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  width="25"
                  height="25"
                  fill="#dc3545"
                  className="bi bi-x-circle"
                  viewBox="0 0 16 16"
                  onClick={removeFile(file)}
                  cursor="pointer"
                >
                  <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
                  <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" />
                </svg>
              </div>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
};

export default UploadMultipleImages;

Parent component:

interface ModalType {
  isOpen: boolean;
  toggle: () => void;
  onSubmit: React.FormEventHandler;
  closeModal: () => void;
}

const gamekitURL = urls.ADD_GAMEKIT;

export default function ModalGameKit(props: ModalType) {
  const [enemies, setEnemies] = useState<File[]>([]);
  const [boards, setBoards] = useState<File[]>([]);
  const [statuses, setStatuses] = useState<File[]>([]);
  const { token } = useToken();

  const gameKitData = new FormData();

  const config = {
    headers: {
      Authorization: `Bearer ${token}`,
      accept: "application/json",
      "Content-Type": `multipart/form-data`,
    },
  };

  const handleEnemiesChange = ({
    currentTarget: { files },
  }: React.ChangeEvent<HTMLInputElement>) => {
    console.log(files);

    if (files && files.length) {
      setEnemies((existing) => existing.concat(Array.from(files)));
    }
  };

  const handleBoardsChange = ({
    currentTarget: { files },
  }: React.ChangeEvent<HTMLInputElement>) => {
    if (files && files.length) {
      setBoards((existing) => existing.concat(Array.from(files)));
    }
  };

  const handleStatusesChange = ({
    currentTarget: { files },
  }: React.ChangeEvent<HTMLInputElement>) => {
    if (files && files.length) {
      setStatuses((existing) => existing.concat(Array.from(files)));
    }
  };

  const handleSubmit = () => {
    enemies.forEach((file) => {
      gameKitData.append("Monsters", file);
    });

    boards.forEach((file) => {
      gameKitData.append("GameBoards", file);
    });

    statuses.forEach((file) => {
      gameKitData.append("Statuses", file);
    });

    axios
      .post(
        gamekitURL,
        {
          data: gameKitData,
        },
        config
      )
      .then((response) => {
        if (response.status === 200) {
          //close modal
        }
      })
      .catch((error) => {
        if (error.response) {
          console.log("error: "   error.response.status);
        } else if (error.request) {
          console.log("error: "   error.request);
        } else {
          console.log("error: "   error.message);
        }
      });
  };

  return (
    <div>
      {props.isOpen && (
        <div className="modal__overlay" onClick={props.toggle}>
          <div onClick={(e) => e.stopPropagation()} className="modal__box">
            <form onSubmit={props.onSubmit} className="form">
              <div className="form__body">
                <div className="form__name">
                  <input
                    className="form-control"
                    id="name"
                    placeholder="NAME"
                  />
                </div>
                <div className="form__enemies">
                  <h4 className="form__header">Enemies:</h4>
                  <div className="form__background-img">
                    <ImagesUploader
                    />
                  </div>
                </div>
                <div className="form__boards">
                  <h4 className="form__header">Boards:</h4>
                  <div className="form__background-img">
                    <ImagesUploader
                    />
                  </div>
                </div>
                <div className="form__statuses">
                  <h4 className="form__header">Statuses:</h4>
                  <div className="form__background-img">
                    <StatusUploader
                    />
                  </div>
                </div>
              </div>
              <div className="form__buttons">
                <div className="form__button">
                  <Button
                    color="#B98649"
                    width="80px"
                    children="Submit"
                    type="submit"
                    onClick={handleSubmit}
                  />
                </div>
              </div>
            </form>
          </div>
        </div>
      )}
    </div>
  );
}

I would like to use handleBoardsChange and two others to save my data from child component (images uploader), but I have no idea how to pass it from child to parent. I've seen similar questions, but in every answer the values were saved in parent and methods from child had body in parent, but in my case I want everything to happen in child component and just pass the list of files to parent. Is there any way to solve it?

CodePudding user response:

I don't believe this is possible, you can only pass values down from parent to child, if you want to pass a value from a child to a parent, you may want to look into state management libraries such as Redux, MobX, Zustand ect.

CodePudding user response:

When working on React if you need to update the state of the parent you need to pass a handler function that is tied to the parent down to the child or use a context to allow it to be accessed deeper.

The child component can call the function say setSomeValue that it obtained from a prop or a context.

  • Related