How can I have multiple dropzones on one page that preview multiple images when using react-dropzone?

4.8k views Asked by At

I am using react-dropzone on my app and would like to have multiple dropzones on one page to preview multiple images. For example, I would like to be able to drop a hero image onto a dropzone that displays the hero image on the top of the page. I would then like to drop a different image onto a different dropzone that displays the image in a thumbnail container.

import React, { useState, useMemo, useEffect } from "react";
import Container from "../components/Container";
import { useDropzone } from "react-dropzone";

const Test = () => {
  // Dropzone
  const baseStyle = {
    flex: 1,
    display: "flex",
    flexDirection: "column",
    alignItems: "center",
    padding: "20px",
    borderWidth: 2,
    borderRadius: 2,
    borderColor: "#eeeeee",
    borderStyle: "dashed",
    backgroundColor: "#fafafa",
    color: "#bdbdbd",
    outline: "none",
    transition: "border .24s ease-in-out",
  };

  const activeStyle = {
    borderColor: "#2196f3",
  };

  const acceptStyle = {
    borderColor: "#00e676",
  };

  const rejectStyle = {
    borderColor: "#ff1744",
  };

  const [files, setFiles] = useState({});
  const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({
    accept: "image/*",
    onDrop: (acceptedFiles) => {
      console.log(acceptedFiles);
      setFiles(
        Object.assign(acceptedFiles[0], {
          preview: URL.createObjectURL(acceptedFiles[0]),
        })
      );
    },
  });

  const style = useMemo(
    () => ({
      ...baseStyle,
      ...(isDragActive ? activeStyle : {}),
      ...(isDragAccept ? acceptStyle : {}),
      ...(isDragReject ? rejectStyle : {}),
    }),
    [isDragActive, isDragReject, isDragAccept]
  );

  useEffect(
    () => () => {
      // Make sure to revoke the data uris to avoid memory leaks
      URL.revokeObjectURL(files.preview);
    },
    [files]
  );

  return (
    <Container>
      {/* This would be the dropzone for the Hero image */}
      <div>
        <div {...getRootProps({ style })}>
          <input {...getInputProps()} />
          <span style={{ fontSize: ".8rem" }}>Drop hero image here, or click to select file</span>
        </div>
      </div>

      {/* This would be the dropzone for the Thumbnail image */}
      <div>
        <div {...getRootProps({ style })}>
          <input {...getInputProps()} />
          <span style={{ fontSize: ".8rem" }}>Drop hero image here, or click to select file</span>
        </div>
      </div>

      {/* This would be where the Hero image is displayed */}
      <img
        style={{ width: "600px", height: "200px", margin: "0", display: "block" }}
        src={files.preview ? files.preview : "https://via.placeholder.com/600x200"}
        alt="Hero Image"
      />

      {/* This would be where the Thumbnail image is displayed */}
      <img
        style={{ width: "600px", height: "200px", margin: "0", display: "block" }}
        src={files.preview ? files.preview : "https://via.placeholder.com/600x200"}
        alt="Thumbnail Image"
      />
    </Container>
  );
};

export default Test;

I'm guessing I need to modify the onDrop function but I can't figure how to do this. Any help would be greatly appreciated!

5

There are 5 answers

0
Kassio On

Here is the solution:

  const { getRootProps, getInputProps, isDragActive } = 
  useDropzone({
  onDrop,
  maxFiles: 1,
  accept: {
    "image/jpeg": [],
    "image/png": [],
    "image/jpg": [],
    "image/webp": [],
  },
});

const {
  getRootProps: getRootPropsGallery,
  getInputProps: getInputPropsGallery,
  isDragActive: isDragActiveGallery,
} = useDropzone({
  onDrop: onDropGallery,
  maxFiles: 8,
  accept: {
    "image/jpeg": [],
    "image/png": [],
    "image/jpg": [],
    "image/webp": [],
  },
});
0
Message Akunna On

you are using the Dropzone hook and that will make it difficult to achieve what you are intending to, use the Dropzone component instead and declare state for each Dropzone input you intend to use.

import React, { useState } from "react";
import Container from "../components/Container";
import Dropzone from "react-dropzone";

const Test = () => {
    const [heroFiles, setHeroFiles] = useState([]);
    const [thumbnailFiles, setThumbnailFiles] = useState([]);
    
    return (
        <Container>
            {/* This would be the dropzone for the Hero image */}
            <Dropzone  onDrop={(acceptedFiles) => {
                  setHeroFiles(acceptedFiles.map(file => Object.assign(file, {
                      preview: URL.createObjectURL(file)
                  })));
               }} name="heroImage" multiple={false}>
               {({getRootProps, getInputProps}) => (
                   <div {...getRootProps({className: 'dropzone'})}>
                       <input {...getInputProps()} />
                       <span style={{ fontSize: ".8rem" }}>
                            Drop hero image here, or click to select file
                       </span>
                   </div>
               )}
          </Dropzone>
          {/* This would be the dropzone for the Thumbnail image */}
          <Dropzone  onDrop={(acceptedFiles) => {
                  setHeroFiles(acceptedFiles.map(file => Object.assign(file, {
                      preview: URL.createObjectURL(file)
                  })));
               }} name="heroImage" multiple={false}>
               {({getRootProps, getInputProps}) => (
                   <div {...getRootProps({className: 'dropzone'})}>
                       <input {...getInputProps()} />
                       <span style={{ fontSize: ".8rem" }}>
                            Drop hero image here, or click to select file
                       </span>
                   </div>
               )}
            </Dropzone>
            {/* This would be where the Hero image is displayed */}
            <img style={{ width: "600px", height: "200px", margin: "0", display: "block" }} src={heroFiles.length > 0 ? heroFiles[0].preview : "https://via.placeholder.com/600x200"} alt="Hero Image"/>
            {/* This would be where the Thumbnail image is displayed */}
            <img style={{ width: "600px", height: "200px", margin: "0", display: "block" }} src={thumbnailFiles.length > 0  ? thumbnailFiles[0].preview  : "https://via.placeholder.com/600x200"} alt="Thumbnail Image"/>
        </Container>
    );
}

export default Test;

this is supposed to take care of your issues.

0
smile On

You should use two separate filenames on the state one for hero one for thumbnail and manage each of them for each dropzones something like this:

const [heroFiles, setHeroFiles] = useState({});
const [filesThumb, setFilesThumb] = useState({});
0
Mohamed Ait lQadi On
  • Create two separate file variables and handle them separately.

  • use two separate getRootsProps and getInputProps on both inputs.

     const [file, setFile] = useState({}); 
     const [fileGallery, setFileGallery] = useState({}); 
    
     const { getRootProps:getRootfileProps, getInputProps:getInputfileProps  } = useDropzone({
         accept: 'image/*',
         onDrop: (acceptedFile) => {
             setFile(
                 Object.assign(acceptedFile[0], {
                     preview: URL.createObjectURL(acceptedFile[0]),
                 }),
             );
         },
     });
    
     const { getRootProps:getRootGalleryProps, getInputProps:getInputGalleryProps } = useDropzone({
         accept: 'image/*',
         onDrop: (acceptedFile) => {
             setFileGallery(
                 Object.assign(acceptedFile[0], {
                     preview: URL.createObjectURL(acceptedFile[0]),
                 }),
             );
         },
     });
    
    
     return (
         <Container>
         {/* This would be the dropzone for the Hero image */}
         <div>
             <div {...getRootfileProps({ style })}>
             <input {...getInputfileProps()} />
             <span style={{ fontSize: ".8rem" }}>Drop hero image here, or click to select file</span>
             </div>
         </div>
    
         {/* This would be the dropzone for the Thumbnail image */}
         <div>
             <div {...getRootGalleryProps({ style })}>
             <input {...getInputGalleryProps()} />
             <span style={{ fontSize: ".8rem" }}>Drop hero image here, or click to select file</span>
             </div>
         </div>
    
         {/* This would be where the Hero image is displayed */}
         <img
             style={{ width: "600px", height: "200px", margin: "0", display: "block" }}
             src={files.preview ? files.preview : "https://via.placeholder.com/600x200"}
             alt="Hero Image"
         />
    
         {/* This would be where the Thumbnail image is displayed */}
         <img
             style={{ width: "600px", height: "200px", margin: "0", display: "block" }}
             src={files.preview ? files.preview : "https://via.placeholder.com/600x200"}
             alt="Thumbnail Image"
         />
         </Container>
     );
     };
    
     export default Test;
    
0
M Imran On

you need to create two separate file variables and handle them separately. also you are using same getRootsProps and getInputProps on both inputs which is not correct.

const [file, setFile] = useState({}); 
  const [fileGallery, setFileGallery] = useState({}); 
    const { getRootProps:getRootfileProps, getInputProps:getInputfileProps  } = useDropzone({
        accept: 'image/*',
        onDrop: (acceptedFile) => {
          setFile(
            Object.assign(acceptedFile[0], {
              preview: URL.createObjectURL(acceptedFile[0]),
            }),
          );
        },
      });
      const { getRootProps:getRootGalleryProps, getInputProps:getInputGalleryProps } = useDropzone({
        accept: 'image/*',
        onDrop: (acceptedFile) => {
          setFileGallery(
            Object.assign(acceptedFile[0], {
              preview: URL.createObjectURL(acceptedFile[0]),
            }),
          );
        },
      });