React Dropzone: A Thorough Introduction
Tags: react, reactnative, javascript, typescript
Published: 2023-05-14 11:13:21

The uploading of files is a common feature found in most web applications, and **drag and drop functionality** has become a convenient way to achieve this. To implement this feature in a React application, the **react-dropzone module** is a popular choice due to its rich set of features and customization options that can be tailored to suit individual needs. 

This article aims to provide an in-depth understanding of the **react-dropzone module**, explore various methods of utilizing its functionality to implement **drag and drop**, and provide examples of customizing its behavior.

## React Dropzone Module: An Introduction

**React Dropzone**, also known as **react-dropzone**, is a React module that allows for easy creation of drag-and-drop functionality within a React application. This module simplifies the user's file uploading experience, enabling them to drag files from their file explorer and drop them into the application for immediate upload. With a plethora of customization options, **React Dropzone** is a feature-rich module. Users can restrict the types of files that can be uploaded, as well as upload multiple files simultaneously, among other capabilities.

## How to Install, and Modeling a Basic React Dropzone File Picker

To add the **react-dropzone** module to an application, we can utilize the following command.

`npm install react-dropzone`

This module is ideal for any application that involves file uploading, such as a university entrance application website, a job application site, or a post uploading platform. Our initial step is to construct a basic file picker with **React Dropzone**.

Initially, we should import the **React Dropzone library** below all other imports in the file. Following this, we can include the **React Dropzone** component in the render method's return statement, as well as an **onDrop** method above it. The resulting code should resemble the following:

```javascript
import React, { Component } from 'react';
import Dropzone from 'react-dropzone'

class App extends Component {

  onDrop = (acceptedFiles) => {
    console.log(acceptedFiles);
  }

  render() {
    return (
      <div className="text-center mt-5">
        <Dropzone onDrop={this.onDrop}>
          {({getRootProps, getInputProps}) => (
            <div {...getRootProps()}>
              <input {...getInputProps()} />
              Click me to upload a file!
            </div>
          )}
        </Dropzone>
      </div>
    );
  }
}

export default App;
```

**React Dropzone** requires only one method to be passed into the **onDrop** prop to handle file selection. To maintain simplicity, we will name the method **onDrop** to match the prop name.

The **onDrop** method has a single parameter, acceptedFiles, which we will currently log to the console for testing purposes. After saving the component, we can open our browser and navigate to our running React app.

Clicking the text label will open a **file picker** window, which is a great start! However, selecting a file to upload will not have any effect at this time. To make file uploads functional, we need to send the file to a server, which will be covered in a future tutorial.

## Render Props

**React Dropzone**'s appearance may appear distinct from other React components you've encountered because it employs the Render Props technique.

The Render Prop Function modifies the HTML within the **Dropzone component** based on its current state.

To demonstrate this concept, we'll add a variable to the Render Prop function named **isDragActive**. This variable grants us access to the **Dropzone** component's current drag state. 

With the **isDragActive** state available, we can alter the text value of the label to display something distinct when a file is dragged over the component.

```javascript
<Dropzone onDrop={this.onDrop}>
  {({getRootProps, getInputProps, isDragActive}) => (
    <div {...getRootProps()}>
      <input {...getInputProps()} />
      {isDragActive ? "Drop it like it's hot!" : 'Click me or drag a file to upload!'}
    </div>
  )}
</Dropzone>
```

The code snippet mentioned above demonstrates an inline conditional that examines whether **isDragActive** is true or not. If it is true, the text **"Drop it like it's hot"** is displayed, and if it is false, the text **"Click me or drag a file to upload!"** is shown. 

## To Allow Specific Types of Files

At present, our file selector permits us to choose any file type to upload.

However, depending on the purpose of the file picker, we may want to restrict the selection to specific file types, such as only **.JPG** files, or only **.XLS** and .**DOCX** files.

To achieve this, we can use the accept prop and add it after onDrop within the Dropzone component declaration.

```javascript
<Dropzone
  onDrop={this.onDrop}
  accept="image/png, image/gif"
>
...
</Dropzone>
```

File types are represented as **MIME types**, and Mozilla provides a comprehensive list of **MIME types**. 

To enhance the user experience of our file picker, we can display a message when the user attempts to upload a file type that is not accepted. 

We can accomplish this by adding another render prop called isDragRejected and modifying the inline conditional logic accordingly.

```javascript
<Dropzone
  onDrop={this.onDrop}
  accept="image/png"
>
  {({getRootProps, getInputProps, isDragActive, isDragReject}) => (
    <div {...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!"}
    </div>
  )}
</Dropzone>
```

Finally, we can save the file and test it by attempting to drag a .JPG file onto the file picker in the browser. When we do so, a message is displayed indicating that our file picker does not accept that type of file.

## The Highest and Lowest File Size

Setting a limit on the file size is crucial to prevent users from uploading excessively large files and overloading your server.

React Dropzone offers two props, minSize and maxSize, which allow you to set the minimum and maximum file size in bytes. 

To specify a maximum file size of 5MB or less, add the minSize and maxSize props to the Dropzone component below the accept prop.

```javascript
App.js
<Dropzone
  onDrop={this.onDrop}
  accept="image/png"
  minSize={0}
  maxSize={5242880}
>
  ...
</Dropzone>
```

To enhance the user experience, let's add some code to our file input component that verifies the maximum file size and displays an error message if the uploaded file exceeds the limit. 

```javascript
render() {
  const maxSize = 1048576;
  return (
    <div className="text-center mt-5">
      <Dropzone
        onDrop={this.onDrop}
        accept="image/png"
        minSize={0}
        maxSize={maxSize}
      >
        {({getRootProps, getInputProps, isDragActive, isDragReject, rejectedFiles}) => {
          const isFileTooLarge = rejectedFiles.length > 0 && rejectedFiles[0].size > maxSize;
          return (
            <div {...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>
          )}
        }
      </Dropzone>
    </div>
  );
}
```

Despite the appearance of a significant amount of code, it is only a few lines (usually less than 10).

Here are the instructions:

- We create a constant named "maxSize" and set its value to 1MB at the beginning of the render method.
- We use the "maxSize" constant in the "maxSize" prop of the Dropzone component.
- We add a new render prop called "rejectedFiles" to the Dropzone component.
- After the render prop function in the Dropzone component, we define another constant called "isFileTooLarge". It retrieves the first file from the "rejectedFiles" array and checks if its size is greater than the "maxSize" constant we defined earlier.
- We then use a conditional statement to check if "isFileTooLarge" is true and display the message "File is too large." in red.

Let's test the functionality in the browser!

More than One File in Action!

The final feature we will discuss about **React Dropzone** is the capability to upload multiple files. It is a straightforward process and does not involve any changes to the render prop function. To enable this feature, we just need to add the **"multiple"** prop to the **React Dropzone** component declaration.

```javascript
<Dropzone
  onDrop={this.onDrop}
  accept="image/png"
  minSize={0}
  maxSize={maxSize}
  multiple
>
  ...
</Dropzone>
```

## How to Use Hooks in React Dropzone

The release of React Hooks and the updated version of **react-dropzone library** with the custom useDropzone Hook has prompted a rewrite of the entire component as a functional one. 
This involves using the **useDropzone** custom hook provided by **react dropzone**.

```javascript
import React, { useCallback } from 'react';
import { useDropzone } from 'react-dropzone'

const App = () => {
  const maxSize = 1048576;

  const onDrop = useCallback(acceptedFiles => {
    console.log(acceptedFiles);
  }, []);

  const { isDragActive, getRootProps, getInputProps, isDragReject, acceptedFiles, rejectedFiles } = useDropzone({
    onDrop,
    accept: 'image/png',
    minSize: 0,
    maxSize,
  });

  const isFileTooLarge = rejectedFiles.length > 0 && rejectedFiles[0].size > maxSize;
  
  return (
    <div className="container text-center mt-5">
      <div {...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>
    </div>
  );
};

export default App;
```

## How to Show a List of Accepted Files

The availability of React Hooks and the updated version of **react-dropzone** library, which includes a custom **useDropzone Hook**, has resulted in a complete rewrite of the component as a functional one.
This rewrite entails utilizing the **useDropzone** custom hook provided by **react-dropzone**.

```javascript
...
<ul className="list-group mt-2">
  {acceptedFiles.length > 0 && acceptedFiles.map(acceptedFile => (
    <li className="list-group-item list-group-item-success">
      {acceptedFile.name}
    </li>
  ))}
</ul>
...
```

## Conclusion

To sum up, **react-dropzone** is a widely used React module that enables drag and drop functionality in web applications. This package provides many customization options, including the ability to limit the size of uploaded files and allowing specific file types to upload. Additionally, the package offers the ability to upload multiple files and display a list of uploaded files easily. With **react-dropzone**, we don't have to rely on the HTML drag and drop API, which was previously the most common way to upload files using drag and drop.