14

Using React v16.1, on a local development server. I have a simple component to upload a file here:

class FileImporter extends React.Component {
    constructor(props) {...}
    importFile(e) {
      // import the file here, not firing
    }
    render() {
      return (<input type="file" onChange={this.importFile.bind(this)} />);
    }
}    

The function importFile never fires. I've tried binding onChange in the following ways:

onChange={e => this.importFile(e)}
onChange={this.importFile.bind(this)}
onChange={this.importFile}

I've tried using react-file-reader-input and react-file-reader, and just a raw input tag like in the snippet. None of them fire the onChange handler. The system file upload dialog shows up, but on selecting a file nothing happens. How can I get the onChange event to fire?

11 Answers11

31

For others who google here: I had a similar issue and the solution I found was that my <input> was being removed from the DOM after clicking the button to open the file picker. (It was inside a drop down.)

Clicking the input still showed the file picker, but that also caused the drop down to hide, thus removing the input from the DOM. After selecting a file the onChange never fired. I moved the input to a place that it wouldn't be removed from the DOM and things began to work again.

user3788955
  • 486
  • 4
  • 11
  • Any real solution instead of this workaround? I got this problem because my input is in a popover. Yes, I could confirm the problem went away when I moved the input outside of the popover. However, I really need to make it work when it's inside of the popover. – newman Dec 26 '20 at 04:02
  • 1
    I don't know if I would call this a workaround. No in the DOM, browser can't do anything. To you question though: Another approach that comes to mind could be to use styles to set display:none when you want the popup to be hidden. That way the popup is still in the DOM, as is the . (* I didn't test this myself, but should work *) – user3788955 Jan 01 '21 at 20:25
  • after 2 hours of heavy workout, i found this answer. Certainly the element should remain in DOM – Dila Gurung Jun 06 '21 at 07:39
  • My problem was: whenever I clicked on file input, file picker would open. But file would not selected on "onchange" event and file picker would keep getting opened. My design was like, the was inside the div box. Earlier I styled the input file like this css (position: absolute; z-index: 0; opacity: 0; filter: alpha(opacity=0)). The reason for this style is, I wanted to make input focusable as I was under impression that control is only focusable if it has space on the page. But after reading this comment, I kept the input as only "display:none" and it worked :) – Priyanka Lad Jul 29 '21 at 18:59
  • I still don't understand though as in why file picker was keep getting reopened – Priyanka Lad Jul 29 '21 at 19:04
  • This got me... import { Accordion } from 'react-bootstrap' – rom Jan 23 '22 at 23:04
  • Thanks, this saved me quite some time. We had an input within a react-contexify popup menu. For some reason, the problem surfaced only in production build, where contexify seems to be more "sensitive" to clicks and did not give enough time for the `onChange` event to fire. Instead the issue did not show in dev build: the menu still closes and is removed from DOM, but the event fires. – superjos Jan 28 '22 at 01:30
11

Use onInput instead of onChange

                <div className="profile-form-buttons centercontents">
                    <input className="form-compo"
                        type="file" 
                        name="file-uploader" 
                        id="file-uploader"  
                        onInput={imageUpdate}
                        style={{display:"none"}}
                        />
                </div>
Mars Araullo
  • 119
  • 1
  • 2
11

I had another case where the input was hidden and I had a button which triggers the file selection dialog for the input.

The app showed validations on the file(Don't worry about this, may be just consider a case where I told user to do some changes in the file and reupload).

So when user tried to re-upload the same file again, it did not trigger on change the second time. But if user chose a different file, it worked.

This was because I did not clear the file input's value the first time after the first time user upload was incorrect. So somehow browser thought that the file is same so do not trigger onchange.

Interestingly, when I set input's value to blank string(Specifically in my react project, I had a ref to the input, so I did inputRef.current.value = '' in react) and it worked.

Mihir Bhende
  • 8,677
  • 1
  • 30
  • 37
8

I have experienced the same issue. The problem was that I have the same id for the multiple inputs. Once I've set a unique id for each input problem was solved. Thanks

<label htmlFor="file-input-id">Upload</label>
<input type="file" id="file-input-id" onChange={this.onSelectImage} />
Roman Grinev
  • 929
  • 9
  • 18
2

When firing the event for the second time, with the same file selected as the first time, onInput/onChange doesn't work for me. I tried to analyze the root cause.Event type onInput/onChange doesn't matter. If the same file is selected for the second time, the input component retains the old filename in component memory, so onInput/onChange is not triggered.

Solution: Clear the input element's current value

const fileInput = useRef<HTMLInputElement>(null);
const handleChange = () => {
      // SOME LOGIC
      fileInput.current.value = '';
};
Archit Gargi
  • 634
  • 1
  • 8
  • 24
1

It works here:

class FileImporter extends React.Component {    
    importFile(e) {
      console.log("fired.");
    }
    render() {
      return (<input type="file" onChange={this.importFile.bind(this)} />);
    }
}

ReactDOM.render(<FileImporter />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app"></div>

So it must be something else you're doing or some other code. I know this is a different version of React, but it should work the same for you.

Jonathan Rys
  • 1,782
  • 1
  • 13
  • 25
  • I confirmed that your code still works on React 16.3.2, so there were no breaking changes that could be affecting your code. This code works. What does the code inside ```importFile``` do? ```console.log``` works, have you tried that? – Jonathan Rys May 11 '18 at 22:14
1

For me the problem was, I was selecting the same file again which prevent from triggering the event because I was selecting it in a bootstrap modal where file name was already chosen. So on Closing modal I clear the modal values

e.target.value = null;

This answer might be helpful How to clear input type file values

Ghias Ali
  • 257
  • 2
  • 10
  • Using Typescript, I got the error `Type 'null' is not assignable to type 'string'.` but `e.target.value = ''` also works – Raphael Pinel Apr 14 '23 at 10:53
0

It's by design in Chrome. Set value to empty string and store the file in useState() hook or ref.

Tamir Abutbul
  • 7,301
  • 7
  • 25
  • 53
Chris Panayotoff
  • 1,744
  • 21
  • 24
0

For functional component, we can use onInputCapture or onChangeCapture.

There is not documentation on React but we can track on this Issue - Diff b/w onChange and onInput and StackOverflow - Difference between onChange and onInput. All the examples are about onInput not onInputCapture.

Finally, able to get TS definition onInputCapture and onChangeCapture, React TS.

    onChange?: FormEventHandler<T> | undefined;
    onChangeCapture?: FormEventHandler<T> | undefined;
    onInput?: FormEventHandler<T> | undefined;
    onInputCapture?: FormEventHandler<T> | undefined;

MDN Docs - GlobalEventHandlers.oninput

const FileSelector = () => (
    <input
        type="file"
        onInputCapture={(e) => console.log(e.target)}
        accept={".xlsx"}
        multiple={false}
    />
);
Yogi
  • 1,527
  • 15
  • 21
0

My issue was that the there was a circular dependency within the state of my input component, input controller & main parent form where I kept track of the state. The logic was valid, but React rerendered the input whenever I selected a file which made me lose access to the onChange event. Spent an hour figuring this out. Ended up using Redux to manage this complex state.

Charming Robot
  • 2,460
  • 2
  • 17
  • 34
-5
constructor(props) {
    super(props);
    this.state = {
        value: ''
    }
}
render(){
    return(
       <input type="text" 
              value={this.state.value} 
              onChange={(e) => this.setState({value: e.target.value})}/>
    )
}
Spoody
  • 2,852
  • 1
  • 26
  • 36