fa__
fa__

Reputation: 327

Upload and save list of files with react-hook-form

I am building a website targeted mostly at browsers using Ionic React. I am trying to use a react-hook-form to upload a list of files (among other data) and save them in a FieldArray together with other data.

I have implemented file upload following this answer in Ionic Forum, using an IonButton and an input.

 <input
       type="file"
       id="file-upload"
       style={{ display: "none" }}
       onChange={() => { setFile(index);}}/>
  <IonButton
       onClick={() => {openFileDialog();}}
       disabled={files === undefined || files[index] === undefined}>
     <IonLabel>Upload file</IonLabel>
     <IonIcon slot="start" />
   </IonButton>

Code:

function openFileDialog() {
    (document as any).getElementById("file-upload").click();
  }

const setFile = (index: number) => (_event: any) => {
    console.log(`Getting file for index ${index}`);

    let f = _event.target.files![0];

    var reader = new FileReader();

    reader.onload = function () {
      setValue(`files.${index}.file`, reader.result);
    };

    reader.onerror = function () {
      console.log("File load failed");
    };

    reader.readAsDataURL(f);
};

Full example code: codesandbox

The file is correctly uploaded but I have not been able to add it to the correct field in the FieldArray. Files are always added to element 0. I assume this is related to input not been modified directly in the form but in function openFileDialog(). As a result, the function onChange() of input does not receive the correct value of index. Is it or is there a different source of error?

A solution would be to wait for the file to be loaded in the method onClick() of IonButton but I cannot send the index when calling (document as any).getElementById("file-upload").click();.

Another solution could be to use only one component for file upload instead of two. However, it looks like Ionic does not have a component for this. IonInput type="file" does not work. The documentation is confusing: "file" does not appear in the list of accepted values for property type but it is mentioned in the description of properties multiple and accepted.

How can I save the file correctly?

Upvotes: 2

Views: 8308

Answers (1)

Aaron Saunders
Aaron Saunders

Reputation: 33345

I found a few issues with your approach, I also removed the reading of the file into a blob, dont think you should do that until the user actually submits since they could delete the file.

First Issue - you were not passing index into this function

  const setFile = (index: number, _event: any) => {
    methods.setValue(`files[${index}].file` as any, _event.target.files[0]);
    methods.setValue(
      `files[${index}].title` as any,
      _event.target.files[0]?.name
    );
  };

The second issue, you were not creating unique identifiers for the upload button, you need to include the index there also

<IonItem>
  <input
    type="file"
    id={`file-upload-${index}`} // <== NEW CODE
    style={{ display: "none" }}
    onChange={(e) => {
      console.log("In input", index);
      setFile(index, e);
    }}
  />
  <IonButton
    onClick={() => {
      openFileDialog(index);  // <== NEW CODE
    }}
    disabled={files === undefined || files[index] === undefined}
  >
    <IonLabel>Upload file</IonLabel>
    <IonIcon slot="start" />
  </IonButton>
</IonItem>

and then you need the index in openFileDialog so you can click the appropriate button.

  function openFileDialog(index: any) {
    (document as any).getElementById("file-upload-" + index).click();
  }

See the complete solution here in this sandbox

Upvotes: 6

Related Questions