input's event.target is null within this.setState [React.js]

Viewed 26218

In my react component I have a file input:

<input type="file" onChange={this.onFileChange.bind(this)} />` 

and my onFileChange is:

onFileChange(e) {
  let file = e.target.files[0];
  this.setState(() => ({ file: e.target.files[0] })); //doesnt work
  // this.setState(() => ({ file })); //works
  // this.setState({ file: e.target.files[0] }); //works
}

This first way of setting the states fails with an error:

Cannot read property 'files' of null

React also gives the following warning:

This synthetic event is reused for performance reasons. If you're 
seeing this, you're accessing the property 'target' on a 
released/nullified synthetic event

But the last two ways of setting the state give no errors or warnings. Why is this happening?

5 Answers

The setState function is executed in asynchronous context.

By the time the state is updated the e.target reference might or might not be gone.

const file = e.target.files[0]; can be used to "remember" the value as in your example.

What is the reason for calling setState with callback ? this.setState({ file: e.target.files[0] }) should do the job.

In your code you are referring to a synthetic event object which no longer holds information about the original DOM event. React reuses the event objects for performance reasons.

Alternatively you can use:

let file = e.target.files[0]; const files = e.target.files this.setState(() => ({ file: files[0] })); //doesnt work

React uses event pooling, you can read more about it in the docs here https://reactjs.org/docs/events.html

setState is an asynchronous function

this.setState(() => ({ file })); // is correct

Very simple/basic example to do the same task:

class Hello extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    file: ''
    };
  }

  render() {
    return <div>
    <input type='file' onChange={(e) => {
    this.setState({file: e.target.files[0]}, () => {
        console.log('state', this.state);
    })
    }} />
    </div>;
  }
}

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

I have added console log when the state will be set it will log the file details. You can see in the log when you select a file the state includes the file data.

To see the console log you need to right click and inspect and see the Console.

Checkout working example here https://jsfiddle.net/1oj3h417/2/

Let me know if you have any doubt

class Example extends React.Component {

  onFileChange = e => {
    let file = e.target.files[0];
    this.setState(() => ({ file: file }));
  }
  render() {
    return <input type="file" onChange={this.onFileChange} />;
  }
}

ReactDOM.render(
  <Example />,
  document.getElementById('root')
);
<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="root">
</div>

Related