1

I'm building a form using useFormState from the react-dom library. One of its fields involves a file input I implemented with react-dropzone. My current code is:

Form

export default function Form() {
 const [files, setFiles] = useState<File[]>([]);

 const initialState = { message: "" };
 const [state, dispatch] = useFormState(sendForm, initialState);

  return (
    <form action={dispatch}>
      <DragDrop />
      <FormActions />
      <Button type="submit">Send</Button>
    </form>
  );
}

DragDrop element (file input)

export default function DragDrop() {
 const onDrop = useCallback((droppedFiles: File[]) => {
   console.log(droppedFiles);
 }, []);

 const { getRootProps, getInputProps } = useDropzone({
   onDrop,
   accept: {
     "application/pdf": [".pdf"],
   },
 });

 return (
   <div {...getRootProps()}>
     <p>Drop or select a file here</p>
     <Button aria-disabled={false}>Select</Button>
     <input {...getInputProps()} name="files" />
   </div>
 );

}

Form action

export async function postFiles(prevState: State, formData: FormData) {
 const files = formData.get("files");

 console.log("files", files);

 //implement files upload

 return {
   message: "Uploaded",
 };

}

The issue arises when I submit the form, and the postFiles function is triggered. When attempting to retrieve the 'file' property from the formData to assign it to the 'files' constant, it returns a null value. The constant ends up with a null, suggesting that there might be an issue with recognizing the files property in the form, despite its declaration in the input element.

Could it be that react-dropzone is overriding the 'name' property? Is there something I'm missing?

Thanks!

1
  • Where are you setting your file that you upload? You have got your onDrop function that console logs the onChange value which is a file but it doesn't pass that file to the form at all.
    – Stretch0
    Commented Mar 2 at 15:56

1 Answer 1

1

I encountered the same problem, here is the solution I came up with.

React-dropzone only deals with drag & drop operations, so the dropped files belong to a DataTransfer object. On the other hand, files added via an input[type=file] are stored in the FormData object.

Both use the FileList interface, but they are different, so you have to bridge the gap yourself and add the files of the DataTransfer object to the input.files.

For convenience, I have created a ref to the form:

const formRef = useRef<HTMLFormElement>(null);

In the onDrop function:

const onDrop = useCallback((acceptedFiles: File[], fileRejections: FileRejection[]) => {
    if (acceptedFiles.length === 1) {
        // Add the file to the input[file] element (react-dropzone doesn't do it)
        const dataTransfer = new DataTransfer();
        dataTransfer.items.add(acceptedFiles[0]);
        if (formRef.current) {
            const input: HTMLInputElement | null = formRef.current.querySelector('input[type=file]');
            if (input) {
                input.files = dataTransfer.files;
            }
        }
    // ...
    }
}

Now the dropped file belongs to the form and is accessible in FormData.

Hope this helps!

Not the answer you're looking for? Browse other questions tagged or ask your own question.