5

I am using the following to allow a user to upload a profile photo with react-dropzone:

const FILE_FIELD_NAME = 'files';

const renderDropzoneInput = (field) => {
  const files = field.input.value;
  let dropzoneRef;
  return (
    <div>
      <Dropzone
        name={field.name}
        onDrop={( filesToUpload, e ) => field.input.onChange(filesToUpload)}
        ref={(node) => { dropzoneRef = node; }}
        accept="image/jpeg, image/png"
        maxSize={5242880}
      >
        {({ isDragActive, isDragReject, acceptedFiles, rejectedFiles }) => {
          if (isDragActive) {
            return "This file is authorized";
          }
          if (isDragReject) {
            return "This file is not authorized";
          }
          return acceptedFiles.length || rejectedFiles.length
            ? `Accepted ${acceptedFiles.length}, rejected ${rejectedFiles.length} files`
            : "Try dropping some files.";
        }}
      </Dropzone>

      <button type="button" onClick={() => { dropzoneRef.open() }}>Open File Dialog</button>

      {field.meta.touched &&
        field.meta.error &&
        <span className="error">{field.meta.error}</span>}
      {
        files && Array.isArray(files) && (
        <ul>
          { files.map((file, i) =>
            <li key={i}>
              <img key={i} style={{width: 50, height: 50}} src={file.preview} alt="preview" />
              {file.name}
            </li>
          )}
        </ul>
      )}
    </div>
  );
}

... inside my redux-form:

    <div>
      <label htmlFor={FILE_FIELD_NAME}>Files</label>
      <Field
        name={FILE_FIELD_NAME}
        component={renderDropzoneInput}
      />
    </div>

React-dropzone is currently respecting the maxSize, where it rejects files that are over 5megs. The problem is react-dropzone is not telling the user the file is too big.

How can I update the above to tell the user if the file exceeds the maxSize allowed?

halfer
  • 19,824
  • 17
  • 99
  • 186
AnApprentice
  • 108,152
  • 195
  • 629
  • 1,012

2 Answers2

12
import React, { useState } from "react";
import { useDropzone } from "react-dropzone";

const UploadFile = () => {
  const [errors, setErrors] = useState("");

  const { getRootProps, getInputProps } = useDropzone({
    multiple: false,
    onDrop: (acceptedFiles, fileRejections) => {
      fileRejections.forEach((file) => {
        file.errors.forEach((err) => {
          if (err.code === "file-too-large") {
            setErrors(`Error: ${err.message}`);
          }

          if (err.code === "file-invalid-type") {
            setErrors(`Error: ${err.message}`);
          }
        });
      });
    }

  return (
    <div{...getRootProps()}>
      <input {...getInputProps()} title={title} />
        <p style={{ color: "red", padding: 5, margin: 0, fontSize: 14 }}>
          {errors}
        </p>
    </div>
  );
};
  1. Create a useState for errors as above.

  2. onDrop provides a second array argument 'fileRejections'.

  3. Loop through fileRejections to access the errors array in it.

  4. Then, loop through the errors array to access 'code and 'message'.

  5. Check the error code with if else for 'file-too-large' or 'file-invalid-type'. Which are the most common error codes.

  6. Use setState in the if else block to set the err.message to the error state.

  7. Display the error state in 'p' tags inside your dropzone's main div.

This works very well when multiple is set 'false'. For multiple file errors you will have to use an arrayState I guess. I haven't really looked into it.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Tom Bombadil
  • 3,455
  • 1
  • 13
  • 24
  • Where do you specify the accepted formats and size here? – Borbag May 12 '22 at 13:45
  • I am not too sure if I understood the question properly. But, this link may be of use to you. https://react-dropzone.org/#!/Accepting%20specific%20file%20types – Tom Bombadil May 14 '22 at 07:58
1

you can have the current size for every file after loaded, and compare with your constant. i don't know if file has a size props, but i suppose that it's included in props. Code should looks like:

const FILE_FIELD_NAME = 'files';

const ErrorMessage = ({ children }) => (
  <div
    style={{
      fontStyle: 'italic',
      color: 'red',
      }}
    >
    {children}
  </div>
)

const renderDropzoneInput = (field) => {
  const files = field.input.value;
  let dropzoneRef;
  const MAX_SIZE = 5242880;
  return (
    <div>
      <Dropzone
        name={field.name}
        onDrop={( filesToUpload, e ) => field.input.onChange(filesToUpload)}
        ref={(node) => { dropzoneRef = node; }}
        accept="image/jpeg, image/png"
        maxSize={MAX_SIZE}
      >
        {({ isDragActive, isDragReject, acceptedFiles, rejectedFiles }) => {
          if (isDragActive) {
            return "This file is authorized";
          }
          if (isDragReject) {
            return "This file is not authorized";
          }
          return acceptedFiles.length || rejectedFiles.length
            ? `Accepted ${acceptedFiles.length}, rejected ${rejectedFiles.length} files`
            : "Try dropping some files.";
        }}
      </Dropzone>

      <button type="button" onClick={() => { dropzoneRef.open() }}>Open File Dialog</button>

      {field.meta.touched &&
        field.meta.error &&
        <span className="error">{field.meta.error}</span>}
      {
        files && Array.isArray(files) && (
        <ul>
          { files.map((file, i) =>
            <li key={i}>
              {file.size > MAX_SIZE ?  (
                  <ErrorMessage>
                    {'file is too big, try with another file'}
                    {file.name}
                  </ErrorMessage>
                ) : (
                  <React.fragment>
                    <img key={i} style={{width: 50, height: 50}} src={file.preview} alt="preview" />
                    {file.name}
                  </React.fragment>
                ) 
              }
            </li>
          )}
        </ul>
      )}
    </div>
  );
}
Eruz Apodaca
  • 556
  • 5
  • 7