82

I have a react component which renders a <input type="file"> dom to allow user select images from browser. I found that its onChange method not called when I select the same file. After some searching, someone suggests using this.value=null or return false at the end of the onChange method but I have tried it doesn't work.

Below is my code:

<input id="upload" ref="upload" type="file" accept="image/*"
           onChange={(event)=> { this.readFile(event) }}/>

Below is what I tried:

<input id="upload" ref="upload" type="file" accept="image/*"
               onChange={(event)=> { 
                   this.readFile(event) 
                   return false
              }}/>

Another one is:

<input id="upload" ref="upload" type="file" accept="image/*"
           onChange={(event)=> { 
               this.readFile(event) 
               this.value=null
          }}/>

I believe above solutions work for jquery but I don't know how to let it work in reactjs.

diiN__________
  • 7,393
  • 6
  • 42
  • 69
Joey Yi Zhao
  • 37,514
  • 71
  • 268
  • 523
  • This is just browser behaviour not to do with react, the file didn't change. See http://stackoverflow.com/questions/4109276/how-to-detect-input-type-file-change-for-the-same-file – Dominic Sep 14 '16 at 08:42
  • Does this answer your question? [HTML input file selection event not firing upon selecting the same file](https://stackoverflow.com/questions/12030686/html-input-file-selection-event-not-firing-upon-selecting-the-same-file) – Kalle Richter May 11 '20 at 19:19

13 Answers13

151

Dup of this thread

<input id="upload" ref="upload" type="file" accept="image/*"
           onChange={(event)=> { 
               this.readFile(event) 
          }}
        onClick={(event)=> { 
               event.target.value = null
          }}

/>
Community
  • 1
  • 1
James Bowler
  • 2,194
  • 2
  • 17
  • 23
  • 4
    This worked for me! I do recommend not using inline functions but the concept works. – Luis Colón Feb 16 '17 at 17:41
  • 11
    With TypeScript, `event.target.value = null` I got the compilation error `TS2322: Type 'null' is not assignable to type 'string'.` But I was able to say `event.target.value = ""` and that worked as per https://stackoverflow.com/a/56258902/2848676 – Michael Osofsky Jan 25 '20 at 04:05
22

I think this in your function does not refer to input field. Try using event.target instead.

<input id="upload" ref="upload" type="file" accept="image/*"
           onChange={(event)=> { 
               this.readFile(event) 
               event.target.value=null
          }}/>
Skriptotajs
  • 844
  • 6
  • 15
19

For me actually worked: event.currentTarget.value = null

    onClick={(event)=> { 
               event.currentTarget.value = null
          }}
Ernane Luis
  • 353
  • 2
  • 9
12

After hours of online research this worked for ME on Reactjs

<input id="upload" ref="upload" type="file" accept="image/*"
           onChange={(event)=> { 
               //to do
          }}
        onClick={(e)=> { 
               e.target.value = null
          }}

/>
Muneeb Ejaz
  • 696
  • 6
  • 11
9

My React version: 16.2.0

@jbsmoove solution works fine for all browsers except IE11.

As for IE11, in case if we Open some file and in second time we press Cancel insteed of Open file it shows empty screen and in console we see:

Unable to get property 'name' of undefined or null reference

So I've come up with another solution which works perfect even in IE11:

<input id="upload" ref="upload" type="file" accept="image/*"
      onInput={(event)=> { 
           this.readFile(event) 
      }}
      onClick={(event)=> { 
           event.target.value = null
      }}
/>

Just use onInput instead of onChange.

Mike Mike
  • 598
  • 7
  • 15
5

Below is my solution. (Using Typescript)

import * as React from "react";

export class FileSelector extends React.Component<undefined, undefined>
{
    constructor(props: any)
    {
        super(props);
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(selectorFiles: FileList)
    {
        console.log(selectorFiles);
    }

    render ()
    {
        return <div>
            <input type="file" onChange={ (e) => this.handleChange(e.target.files) } />
        </div>;
    }
}
Yash Vekaria
  • 2,285
  • 24
  • 24
  • Isn't this answering missing the point in that it should call `event.target.value = ""`. Or more specifically ` {this.handleUploadDemand(ie); ie.target.value = ""}}/>}` – Michael Osofsky Jan 25 '20 at 04:08
1

If we want to update same file(same name) multiple times we will get above error. To avoide we need to assign null value for the currentTarget after first time updated the file.

Ex.

const handleFile = useCallback(e => {

    const MAX_FILE_SIZE = (fileSize.MAX_FILE_SIZE_MANAGE_TAGS * 1024)

    if (e.target.files.length > 0) {

        const uploadedFileSize = e.target.files[0].size / 1024

        if (uploadedFileSize < MAX_FILE_SIZE) {

            uploadFile(e.target.files[0]) // you will get current uploaded value

            e.currentTarget.value = null // this the place where we need to assign null value

        } else {
            console.log('file is large')
        }
    }
}, [ ])



<Button
    className={classes.uploadButton}
    component="label"
>
    Upload File
    <input
        onChange={handleFile}
        type="file"
        accept=".xlsx"
        hidden
    />
</Button>
KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133
1

Typescript way without assertion.

  <input
      id="upload"
      ref="upload"
      type="file"
      accept="image/*"
      onChange={(e: ChangeEvent<HTMLInputElement>) => {
                this.readFile(e)
      }}
      onClick={(e: MouseEvent<HTMLInputElement>) => {
                e.currentTarget.value = "";
      }}
   />
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 14 '23 at 17:31
1

This forces it to 'change' the file each time even if the same file is selected again.

<input type="file" value="" />
0

Use a fallback on-Click method to select the same image. Example:

  <input
    id="upload" 
    ref="upload" 
    type="file" 
    accept="image/*"       
    multiple="false"
    onChange={(e) => this.getDailyImage(e)}
    onClick={(e) => e.target.files[0] && this.getDailyImage(e)}
  />
SM Chinna
  • 341
  • 2
  • 7
0

I usually just use key prop on input to reset it's underlying DOM node between selections (either use some incremental UID counter in change handler or uploading/processing state flag):

const UploadInput = (props) => {
  const [isUploading, setUploading] = useState(false);

  const handleChange = useCallback((async (e) => {
    setUploading(true); // or setUID((old) => old + 1);

    try {
      await ... // handle processing here
    } finally {
      setUploading(false);
    }
  }, [...]);

  return (
    <div className={...}>
      <input key={isUploading} type="file" onChange={handleChange} {...props} />

      {isUploading && <Spinner />}
    </div>
  );
};

Another way might be to get ref of the form and use formRef.reset() after processing (viable if you don't care for form contents after file processing).

ankhzet
  • 2,517
  • 1
  • 24
  • 31
0

I got mine to work by adding the following onchange event listener in my input, using Typescript.

const onInputFileChange = (
  event: ChangeEvent<HTMLInputElement>
): void => {
  const nativeEvent = event.nativeEvent.target as HTMLInputElement;
  const targetEvent = event.target as HTMLInputElement;
  if (targetEvent.files && targetEvent.files[0]) {
    const imageFile = targetEvent.files[0];
    // eslint-disable-next-line no-param-reassign
    nativeEvent.value = "";
  }
};
Akinjiola Toni
  • 658
  • 7
  • 9
0

If you are using multiple file to upload try currentTarget.value

  <input id='uploadFile' type="file" onClick={(e)=> {e.currentTarget.value = null}}  onChange={onChangeGetFilesSelected}  multiple={true} accept=".xlsx,.xls,.doc, .docx,.ppt, .pptx,.txt,.pdf"/>
Sherothkar
  • 109
  • 8