Search code examples
reactjsreact-hooksreact-dropzone

Flickering while dragging a file over drop zone


I'm trying to do the effect where the window shows a drop zone that fills the window only when you drag a file over it and disappear when you cancel the drag or drop the file.

Almost all examples of react-dropzone demonstrate a drop zone that is always visible which is not what I want.

Take a look on this CodeSandbox to understand the problem

https://codesandbox.io/s/boring-buck-2fwm6?file=/src/App.js

Or check the code here:

import React from "react";
import "./styles.css";
import { useDropzone } from "react-dropzone";
import { Box, makeStyles, createStyles } from "@material-ui/core";

const useStyles = makeStyles(theme =>
  createStyles({
    dropZone: {
      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",
      position: "absolute",
      width: "calc(100% - 4px)",
      height: "calc(100% - 4px)",
      zIndex: 10
    }
  })
);
const useDropzoneInternal = () => {
  const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
    noClick: true
  });
  const inputProps = getInputProps();
  const { ref, ...rootProps } = getRootProps();
  return { rootProps, inputProps, ref, open, isDragActive };
};

export default function App() {
  const classes = useStyles();
  let { rootProps, isDragActive, inputProps } = useDropzoneInternal();

  return (
    <div className="App">
      <Box
        {...rootProps}
        display="flex"
        flexDirection="column"
        flexGrow={1}
        position="relative"
      >
        {isDragActive && (
          <Box className={classes.dropZone}>
            <Box>
              <input {...inputProps} />
              {<p>Drop the files here ...</p>}
            </Box>
          </Box>
        )}
        <h1>Hello CodeSandbox</h1>
        <h2>Drag a file here!</h2>
        <h2>Unfortunately, the drop zone appears and disappears</h2>
        <h2>Because the gray area covers the parent</h2>
        <h2>And hijack the onDragEvent</h2>
        <h2>Start editing to see some magic happen!</h2>
        <h2>Start editing to see some magic happen!</h2>
        <h2>Start editing to see some magic happen!</h2>
        <h2>Start editing to see some magic happen!</h2>
        <h2>Start editing to see some magic happen!</h2>
      </Box>
    </div>
  );
}

You can see I'm showing the dropzone when isDragActive is true, but it immediately flips back to false because the newly shown area covers the parent div (and probably cancels the drag event).

How can I get around this? any suggestions?


Solution

  • I found out what was missing and it was silly as I expected, you need to pass the ref to the parent element that decides the boundaries of the drag event.

      let { ref, rootProps, isDragActive, inputProps } = useDropzoneInternal();
    
      return (
        <div className="App" ref={ref}>
    

    or if you want to stick to Material UI component:

    <div className="App">
      <RootRef rootRef={ref}>
        <Box
          {...rootProps}
          display="flex"
          flexDirection="column"
    

    I was under the impressions that it is not required, and it will be assigned if you destruct the {...rootProps} on the parent div.