Search code examples
javascripthtmlreactjsfile-uploaddrag-and-drop

Create controlled input with drag&drop in react


I am trying to create a component to upload a single image, show its preview and delete option. Also, it is important for me to be able to upload from the backend base64 encoded URL into field value. This is what I have at the moment. I pass value from the application state into component props, I've implemented onChange handler so it writes the URL to the state and deleteImage just deletes the URL from state.

const ImageInput = ({ value, onChange, deleteImage }) => (
    <div>
        <input type="file" accept="image/*" onChange={onChange}/>
        { value &&
        (<div>
           <img src={value} alt="" width="50"/>
           <span onClick={deleteImage}>&#10060;</span>
         </div>)
        }
    </div>
);

But now I want to replace the usual input with a drag & drop zone and looking for the easiest way to do this. I would be grateful for any help.

Updated. I've chosen to use the library react-dnd, but I don't fully understand how to use it here. So I have to do something like that first?

import {DndProvider} from "react-dnd";
import {HTML5Backend} from "react-dnd-html5-backend";

const ImageInput = ({ value, onChange, deleteImage }) => (
    <div>
        <DndProvider backend={HTML5Backend}>
           <myDropTarget/>
        </DndProvider>
        { value &&
        (<div>
           <img src={value} alt="" width="50"/>
           <span onClick={deleteImage}>&#10060;</span>
         </div>)
        }
    </div>
);

And then just create such a component.

import React from "react";
import { useDrop } from 'react-dnd'


export function myDropTarget(props) {
    const [collectedProps, drop] = useDrop({
        accept
    })

    return <div ref={drop}>Drop Target</div>
}

But it doesn't work for me. I get an error: React Hook "useDrop" is called in function "myDropTarget" which is neither a React function component or a custom React Hook function


Solution

  • You can do this quite easily with react-uploady:

    import React from "react";
    import Uploady from "@rpldy/uploady";
    import UploadDropZone from "@rpldy/upload-drop-zone";
    import UploadPreview from "@rpldy/upload-preview";
    
    const firstFileOnlyFilter = (file, index) => index === 0;
    
    export default function App() {
      return (
        <Uploady
          destination={{ url: "[upload-url]" }}
          fileFilter={firstFileOnlyFilter}
        >            
            <DropZone>Drag file here</DropZone>
            <br />
            <UploadPreview />
        </Uploady>
      );
    }

    You can see a working example in this sandbox.