input’s event.target is null within this.setState [React.js]
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?
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>